Newsgroup: it.comp.lang.c++
Titolo: Factory pattern

Salve a tutti,
ho questo problema: devo leggere un file, in cui ogni riga viene
parserizzata per creare un oggetto derivato da una classe base (es:
Deriv1 derivato da Base).
Googolando mi sembra di capire che il Factory Pattern e' quello che fa
per me: potrei passare la stringa alla factory, e lei mi restituisce
un Base* della classe derivata opportuna. Fin qui e' corretto?
Sempre googolando ho buttato giu' questo (modello concettuale ridotto
all'osso, non il vero codice), che non funziona (e so perche', ma non
so come correggere):
class Base {
public:
    static Base* Factory(int type) {
        switch(type) {
            case 0:     return new Deriv1(1, 2);
            case 1:     return new Deriv2(10, 20, 30);
            default:    return NULL;
        }
    }
    virtual string toString() const = 0;
protected:
    Base(int foo) : foo_(foo) {}
    virtual ~Base() {}
private:
    int foo_;
}
class Deriv1 : public Base {
public:
    Deriv1(int foo, int bar) :
        Base(foo),
        bar_(bar)
    {}
    virtual string toString() const {...}
private:
    int bar_;
}
class Deriv2 : public Base {
public:
    Deriv2(int foo, int bar, int foobar) :
        Base(foo),
        bar_(bar),
        foobar_(foobar)
    {}
    virtual string toString() const {}
private:
    int bar_;
    int foobar_;
}
Compilando ottengo:
factory_go.h: In static member function `static Base*
Base::Factory(int)':
factory_go.h:14: error: `Deriv1' has not been declared
factory_go.h:16: error: `Deriv2' has not been declared
che e' giusto, perche' la factory sta cercando di costruire oggetti di
una classe che ancora non e' stata dichiarata. Ma non posso ne' fare
una forward declaration (mi serve proprio la dichiarazione della
classe) ne' dichiarare le classi derivate prima della base.
Come risolvere?  Immagino che potrei creare una classe Factory
separata, ma googlando mi sembrava di capire che questo schema (con la
factory dentro la classe base) dovrebbe funzionare...
Thanks!
Pleg


<cut/>
etc...
nel .cpp:
#include "MyFactory.h"
Base* Base::Factory(int type) {
         switch(type) {
             case 0:     return new Deriv1(1, 2);
             case 1:     return new Deriv2(10, 20, 30);
             default:    return NULL;
         }
}
Prova ora.
Non entro nel merito del pattern, ma ci sono molte variazioni che ti 
conviene guardare prima di decidere quale usare.
Ciao
- gio -

Ma stai male, avevo messo tutto il codice nello header file per fare
un rapido test e avere un file solo, "tanto che differenza fa?", ed
ecco che differenza fa... :(
Thanks!
Pleg



Beh, la differenza non sta nel fatto di mettere la definizione della 
funzione Factory nel file .cpp, ma nel fatto di toglierla dalla 
definizione della classe Base e metterla dopo la definizione delle 
classi derivate.
Ovviamente, il fatto di mettere le dichiarazioni negli header e le 
definizioni nei file .cpp aiuta a nascondere l'implementazione delle tue 
funzioni, ma d'altro canto, il fatto di lasciare le definizioni negli 
header darebbe la possibilitą al compilatore di espanderle in linea, 
eventualmente velocizzando il codice finale.
Solo una tra le tante decisioni che bisogna prendere quando si programma :-)
-- 
  FSC - 
http://userscripts.org/scripts/show/59948
 
http://fscode.altervista.org
 - 
http://sardinias.com

Gia', non avevo pensato che spostarla fuori da li' risolveva il
problema :P
BTW: nel codice che ho scritto sopra ho messo il costruttore di Base
come protected per farlo accedere alle classi derivate (che tra
l'altro voglio poter instanziare direttamente, alle volte), ma in
questo specifico caso potrei anche mettere il costruttore public
perche' tanto Base e' pure virtual e non e' istanziabile direttamente,
no? Non c'e' bisogno di metterlo protected/private per costringere il
client ad usare la Factory per istanziare nuovi oggetti, no?
Pleg



Uh... le cose sono un po' mescolate e non necessariamente correlate.
Prima di tutto sě, dato che Base č una ABC (abstract base class) nessuno 
puň crearne delle istanze, č solo possibile derivare da essa, quindi 
puoi anche mettere il costruttore nella sezione public.
Per quanto riguarda il fatto di costringere il codice client ad usare la 
Factory, stai togliendo l'obbligo nel momento stesso in cui concedi 
l'istanziazione diretta delle classi derivate... insomma devi decidere 
se costringere all'uso della fabbrica o meno. Nel caso, rendi i 
costruttori delle classi derivate protetti e dichiara la funzione 
Base::Factory come friend delle classi derivate.
Come ha detto gio nell'altro messaggio, questo č solo uno dei vari 
approcci, ma soprattutto bisogna vedere se hai veramente bisogno di una 
fabbrica in primo luogo - dipende tutto da cosa devi ottenere.
Se lo scopo č solo quello di capire come funziona una fabbrica tutto OK, 
comunque vedi un po' in giro per trovare altri approcci - cerca anche 
informazioni sui c.d. "costruttori virtuali", si tratta di un argomento 
strettamente collegato.




Azz, mi ero completamente dimenticato della introduzione nel post 
originario, non stai frugando le fabbriche tanto per fare, ne hai 
veramente bisogno, OK, cancella ogni riferimento al "bisogno" dal mio 
post qua sopra ;-)


Thx for the info!
E' vero stavo mischiando un po' di cose, un po' perche' stavo
sperimentando, un po' perche' sono un programmatore "amatoriale" :)
Vada allora per i costruttori derivati protetti e friend.  In effetti
ripensandoci bene non credo di dover costruire delle classi derivate
direttamente, avrebbe poco senso mescolare le due cose.
Grazie ancora!
Pleg



Ma figurati, nessun problema, in un modo o nell'altro siamo tutti in 
fase di apprendimento.
Se sei ancora in circolazione e leggi questo messaggio, mi sono 
dimenticato di farti notare che è buona norma citare i messaggi ai quali 
si risponde per rendere ogni messaggio quanto più possibile 
"indipendente" e leggibile in isolamento (diciamo autosufficiente) - 
basterebbe anche solo un pezzo del post al quale si risponde, con 
inclusa la riga di attribuzione, così come vedi in questo messaggio.
Può essere noioso dover ritornare ai messaggi precedenti per leggere 
quanto è stato detto - per non parlare di quando i messaggi precedenti 
vengono cancellati per qualche motivo, rendendo le risposte 
completamente incomprensibili o quasi ;-)
Buon proseguimento con il C++, chiedi pure qui nel gruppo ogni volta che 
hai la necessità.
