La chiave per "accedere ai membri annidati su livelli di astrazione più elevati" è usare "livelli di astrazione più alti". Non lo stai facendo qui: B a F sono referenziati direttamente all'interno di A . Sono tutti strettamente accoppiati e non vi è alcuna astrazione in corso.
La soluzione è usare l'astrazione, quindi B diventa:
class B
{
public int Value => ...
}
Come B consegna Value diventa un dettaglio di implementazione; cioè non è legato all'API ed è libero di cambiare senza influenzare il codice che chiama b.Value . È astratto.
Quindi se B a G risiede in un assembly e A risiede in un altro, puoi implementarlo come:
public class B
{
...
public int Value => c.d.e.f.g.Value;
}
o potresti eseguire ripetutamente la stessa astrazione, in modo che B non abbia idea di dove C ottiene Value da ecc:
public class B
{
...
public int Value => c.Value;
}
Ogni livello di astrazione riduce l'accoppiamento, ma introduce ripetizione e complessità. Così come per tutto il resto della programmazione, devi raggiungere il giusto equilibrio ogni volta che usi questa tecnica e quel punto di equilibrio cambierà ogni volta.