Newsgroup: it.comp.lang.c++
Titolo: A usa B.....e B usa A

Ciao,
dubbio amletico: se la classe A usa la classe B, e viceversa,
ottengo solo grane oppure posso farlo? e come?
grazie
--
Tiberio Grasso

 grazie
Se nessuna delle due e' astratta, probabilmente si.
Leggi questo articolo
http://www.objectmentor.com/publications/dip.pdf
che ti chiarira' le idee a riguardo.
MM

 ottengo solo grane oppure posso farlo? e come?
Roger Martin, nel suo eccellente testo sull'applicazione pratica
in C++ del classico approccio di Booch alla progettazione OO,
indica abbastanza chiaramente perche` le dipendenze mutue
_fra componenti_ siano deleterie.  Il tema e` sviluppato piu`
a fondo nel testo di Lakos sulla programmazione C++ "su
grande scala" -- ancora una volta, si parla di mutua dipendenza
fra _componenti_, non fra singole classe interne ad un singolo
componente.
Se A e B sono "componenti" separati, il modo pulito di togliere
la mutua dipendenza (trasformazione canonica di Martin) e` il
seguente.  Decidi anzitutto quale dei componenti e` piu`
stabile; non vogliamo che un componente piu` stabile venga
a dipendere da uno meno stabile, mentre il verso opposto e` 
OK. Sinonimo di "piu` stabile" in questo contesto e` "a basso
livello" (cfr. Martin) -- e` OK se un alto livello dipende da un
basso livello (anzi, e` inevitabile che vi siano questo tipo di
dipendenze), e` inaccettabile che un basso livello dipenda da
un alto livello.  Quindi diciamo (per ragioni che lascio al lettore
inferire) che il componente (classe) A e` di livello Alto, quello
B di livello Basso; "di natura" (dal primo schizzo del progetto)
avremmo A dipendente da B, e B dipendente da A; quest'ultima
dipendenza, quella di B da A, e` quella che dobbiamo togliere.
Definiamo dunque una _interfaccia_ (classe-base puramente
astratta) I, che definisce (come metodi virtuali puri) tutte le
caratteristiche di A su cui B vorrebbe fare affidamento.
Ora possiamo sostituire la dipendenza di B da A con una
dipendenza di B da I.  In quanto "pura interfaccia", I puo`
essere agevolmente resa stabile, basta progettarla bene.
D'altra parte, per ipotesi B e` stabile, quindi, visto che I
esprime le feature che B usa, anche I sara` almeno
altrettanto stabile di B.
Naturalmente, B ricevera` e terra` una o piu` I per
puntatore ovvero per riferimento, visto che I, essendo
astratta, non puo` essere tenuta ne` passata per valore.
A "implementa" I, cioe` eredita da I facendo override di
tutti i suoi metodi virtuali puri con versioni concrete.
Quindi: B dipende da I (perche` la usa); A dipende da I
(perche` la implementa); A dipende da B.  Nessun ciclo,
tutte le dipendenze sono in direzione "sana", tutto OK.
Da notare che questa trasformazione canonica puo`
lasciare un problema se la dipendenza di B da A, oltre
che relativa al "ricevere" oggetti di tipo A da fuori e/o
"tenerli", era anche relativa al "creare" oggetti di tipo
A.  Questo ci conduce direttamente alle "creational
pattern" del testo "Design Patterns" (Gamma, Vlissides,
ecc), come la pattern Abstract Factory.  Posso
spiegare piu` in dettaglio, se occorre.
Martin chiama questa trasformazione "inversione di
dipendenza", che non mi sembra perfettamente
descrittivo; preferisco chiamarla "trasformazione di
Martin", rendendogli cosi`, tra l'altro, onore:-).
Se A e B sono parte dello stesso componente, non e`
necessariamente il caso di spezzare la dipendenza
ciclica.  Da notare che e` normale e sanissimo che si
divida un componente in piu` classi, cfr. ancora una
volta Martin sul tema, e anche Lakos ha qualcosa da
dire in merito; naturalmente le molteplici classi in cui
si "spezza" un singolo componente sono spesso molto
accoppiate fra loro, comprese dipendenze cicliche,
relazioni "friend", eccetera; non per questo si deve
necessariamente "aggrumare" la molteplicita` di classi 
in una classe sola!
La dipendenza mutua fra A e B, entro lo stesso
componente, non e` un problema, purche`
naturalmente, in almeno uno dei due "versi",
l'uso sia interamente per riferimento (o puntatore),
o limitato al passaggio-per-valore di argomenti (e
valori di ritorno); A e B non possono "tenersi" a
vicenda per valore (incluso ereditare ciascuna
dall'altra:-).
Sintatticamente, la cosiddetta "dichiarazione in
avanti", ovvero "forward declaration", della forma:
class A;
e` quella che permette le dipendenze cicliche.
Questa dichiarazione lascia A "incompleta", quindi
A non puo` essere _usata_ sin quando non sara`
stata completamente _definita_, ma per intanto
si possono avere tutti gli usi "sani" suddetti, come
la dichiarazione di puntatori e reference ad A, o di
argomenti per valore (e valori di ritorno) di tipo A.
Ad esempio:
class A;
class B {
    A& itsA;
public:
    B(A& anA): itsA(anA) {}
    int useA(A& anotherA);
};
e` corretto; ma se ad esempio l'implementazione
di B::useA() fosse del tipo:
int
B::useA(A& anotherA) 
{
    return itsA.foo()+anotherA.bar();
}
prima di giungere a questa implementazione occorre 
che il compilatore abbia visto l'intera class-definition di
A (presumibilmente comprendente le dichiarazioni dei
suoi metodi foo e bar, tornanti int...:-).
Questo puo` dare alcuni vincoli (o scomodita`) alla
implementazione inline di metodi della classe B.  Non
e`, normalmente, il piu` terribile dei problemi.
Alex

grazie
tib

 che ti chiarira' le idee a riguardo.
_Ottimo_ puntatore -- grazie!  In questo articolo Martin
chiarisce la questione in modo limpido (piu` limpido che
nel suo libro, dove svolgeva "esempi significativi"; qui,
invece, usa "esempi giocattolo" -- la mia preferenza per
quest'ultimo stile ne esce ulteriormente rafforzata!-), e
in effetti corregge delle imperfezioni in cui io ero caduto
(un po' per fretta, un po' per essere passato troppo tempo
dall'ultima rilettura del suo libro, un po' per chiarezza
insufficiente del libro stesso -- ad esempio, qui spiega
con chiarezza perche` chiama la sua trasformazione
"inversione", il che non mi convince appieno ma almeno
finalmente me lo fa capire bene:-) nel mio post inviato
di recente su questo stesso thread.  Ne raccomando
calorosamente la lettura, e ti rinnovo i ringraziamenti
per averne postato la URL.
Alex