Newsgroup: it.comp.lang.c++
Titolo: double dispatching?

Salve a tutti,
ho un problema di oo programming e desidero avere il vostro parere ed un
aiuto...
dunque:
supponiamo di avere due interfacce: per esempio frutto e fiore. Le varie
implementazioni di frutto al momento siano PERA, MELA, POMO; le
implementazioni di fiore siano ROSA, VIOLA.
Supponiamo adesso che esista una relazione, per esempio VaInsiemeCon,
che lega ogni frutto ad ogni fiore. Questa relazione e' codificata in
una matrice come quella seguente:
                    ROSA            VIOLA
PERA            si                    no
MELA          forse                 no
POMO           si                    si
da cui si ricava, ad esempio, che un oggetto di tipo PERA non VaInsieme
con un oggetto di tipo VIOLA e VaInsieme con un oggetto di tipo ROSA.
Supponiamo ora che i frutti e i fiori facciano parte di una libreria; e
che un eseguibile che usa questa libreria abbia una funzione:
string va_insieme(frutto * fr,  fiore * fi);
che ritorna si, no o forse.
Qual'e' la strategia piu' conveniente per codificare questo
comportamento in c++?
Una possibile soluzione e' quella di avere una funzione membro in fiore
(ed eventualemente anche in frutto) che esegue una RTTI:
class fiore
{
public:
    string VaInsieme(frutto * fr) = 0;
}
class ROSA : public fiore
{
    string VaInsieme(frutto * fr)
    {
        PERA * pera;
        MELA * mela:
        POMO * pomo;
        pera = dynamic_cast<PERA> fr:
        mela = dynamic_cast<MELA> fr:
        pomo = dynamic_cast<POMO> fr:
        if(pera) return "si";
        else if(mela) return "forse";
        else if(pomo) return "si";
        else return "idontknow!";
   }
};
e l'eseguibile:
string va_insieme(frutto * fr, fiore * fi) {return fi->VaInsieme(fr);}
Il principale difetto di questa soluzione e' il solito: se viene
aggiunta alla libreria la classe FICO derivata da frutto occorre
modificare il codice di tutte le classi derivate da fiore.
Una seconda soluzione che non fa uso del RTTI consiste nel fare il
cosiddetto "double dispatching" (almeno penso che si tratti di double
dispatching!):
class fiore
{
    string VaInsieme(frutto *) = 0;
}
class frutto
{
    string VaInsieme(ROSA *) = 0;
    string VaInsieme(VIOLA *) = 0;
}
class ROSA : public fiore
{
    string VaInsieme(frutto * fr) { return fr->VaInsieme(this);}
}
class MELA : public frutto
{
    string VaInsieme(ROSA *) {return "forse";}
    string VaInsieme(VIOLA *) {return "no";}
}
e l'eseguibile resta lo stesso (penso!):
string va_insieme(frutto * fr, fiore * fi) {return fi->VaInsieme(fr);}
Questa soluzione ha il difetto principale di non essere ampliabile senza
modificare l'interfaccia frutto: per ogni nuovo fiore occorre aggiungere
una funzione.
La mia domanda e':
esiste un modo per risolvere questo problema senza modificare le
interfacce ne' ricorrere alla RTTI?
Una possibile soluzione consiste nel creare una nuova classe
interfaccia:
class fiorefrutto:
{
    string VaInsieme() = 0;
}
class RosaPomo : public fiorefrutto
{
    string VaInsieme() {return "si";}
}
class RosaMela : public fiorefrutto
{
    string VaInsieme(){return "forse";}
}
etc..
Per quel che riguarda l'eseguibile, ci sono due strategie:
1) mettere il RTTI nella funzione va_insieme():
string va_insieme(frutto* fr, fiore* fi)
{
    POMO * pomo;
    MELA * mela;
    PERA * pera;
    ROSA * rosa;
    VIOLA* viola;
    pomo = dynamic_cast<POMO> fr;
    mela = dynamic_cast<MELA> fr:
    pera = dynamic_cast<PERA> fr;
    rosa = dynamic_cast<ROSA> fi;
    viola = dynamic_cast<VIOLA> fi;
    if(pomo && rosa)
        {
            RosaPomo rp;
            return rp.VaInsieme();
        }
    else if(mela&&rosa)
        {
            RosaMela rm;
            return rm.VaInsieme();
        }
etc.
}
2)
Usare solo oggetti di tipo RosaMela
string va_insieme(fiorefrutto * ff);
Entrambe le cose non mi piacciono, ma forse la migliore e' la soluzione
1), perche' mantiene la separazione tra fiore e frutto (che non sono
logicamente legati tra di loro).
Qualcuno conosce pattern migliori per affrontare e risolvere questo tipo
di problemi? Ho sbagliato o dimenticato qualcosa?
Grazie infinite per il vostro aiuto!!
                Guido Gagliardi

>con un oggetto di tipo VIOLA e VaInsieme con un oggetto di tipo ROSA.
Il tuo problema e' interessante. Mi viene in mente che il linguaggio migliore per questa cosa probabilmente e' il PROLOG che ti permette di inserire nel sistema una serie di fatti e regole. Solo che il prolog e' interpreatato, ha una base dati propria, un motore di ricerca inferenziale proprio, etc.
Sei sicuro che ti serva codificare la cosa nel linguaggio? Non ti conviene mettere in piedi un sistema di insiemi e relazioni dinamico?
Conscio che questa non e' la risposta che cerchi... ti saluto cordialmente.
Antonio Donatiello

Cosi` ad una prima lettura del tuo post, direi che una
soluzione potrebbe essere quella di realizzare una classe
che memorizzi semplicemente delle associazioni.
La classe dovrebbe semplicemente prevedere un meccanismo
per inserire e rimuovere relazioni e per effettuare la verifica
circa la presenza o meno di una relazione.
In sostanza tu avresti le tue gerarchie di oggetti ed una
(unica) istanza della classe relazioni.
Una relazione la puoi memorizzare come coppia di tipi
(a,b); la presenza di (a,b) nell'istanza significa che esiste una
relazione tra il tipo a ed il tipo b.
Una si` fatta soluzione necessita` pero` di un meccanismo per
identificare i tipi delle istanze in gioco, a tal proposito puoi
utilizzare l'RTTI oppure piu` semplicemente dotare le istanze di un
metodo virtuale per l'identificazione del tipo di appartenenza.
Per la verita` nessuna delle due possibilita` mi sembra molto OO,
tuttavia in questo caso un piccolo strappo alla "regola" pare
tornare di grande giovamento.
Un problema di questo schema (di massima) potrebbe essere
dovuto all'unicita` della istanza di classe Relazione che in un
ambiente multiprogrammato induce problemi serializzazione
e sincronizzazione. Possibili alternative potrebbero essere
a) avere piu` istanze che pero` devono essere tenute consistenti ed
     il cui funzionamento e` sostanzialmente quello di un FARM
b) distribuire le relazioni tra le classi di oggetti: ogni classe di 
     oggetti conterra` un membro statico di tipo lista contenente
     solo ed esclusivamente i patner specifici di quella classe.
In entrambi i casi devi essere in grado di identificare il tipo esatto
delle istanze in esame.
Se questa veloce spiegazione non fosse molto chiara provero` a
postare qualche frammento di codice (che al momento non ho
il tempo di produrre).
Paolo
-----------------------------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------------------------