PDA

Visualizza la versione completa : [OOP] Factory Method parametrico


mciasco
23-04-2010, 16:11
Salve,

ho un dubbio sul factory method pattern.
Generalmente lo si usa con un solo parametro, che identifica la classe concreta da istanziare fra quelle possibili.
Tuttavia non mi e' chiara una cosa. Come faccio a passare dei parametri ai veri costruttori delle classi concrete che occorre creare?
Ognuna di esse potrebbe avere tutta una serie di membri da valorizzare. Non e' possibile passare tutto al factory method.

shodan
23-04-2010, 16:27
Non mi mai capitato un caso del genere, per se dovessi risolverlo, userei una classe polimorfa come parametro e la inoltrerei al costruttore concreto. Oppure userei un metodo init() che prende come parametro la classe polimorfa e la usa per settare le varie variabili.
Il problema che se si hanno troppe classi concrete (diciamo 6-7), il tutto diventa complesso da gestire.

mciasco
23-04-2010, 16:41
Infatti quello che mi e' venuto in mente di fare e' di passare al factory method, oltre al tipo di classe concreta da istanziare, anche un'istanza di una classe che mi rappresenti la configurazione per quel tipo di classe concreta.

Cioe' se ho le classi concrete Wine e Beer che derivano dalla classe base Drink, il factory method in questione sara' ad esempio "Drink createDrink(string drinkType, DrinkConfiguration config)".
Ovviamente questo significa che si avranno anche le due classi concrete WineConfiguration e BeerConfiguration che derivano da DrinkConfiguration.

Solo che a questo punto il problema ricade su come istanziare la corretta istanza di DrinkConfiguration. In pratica da qualche parte c'e' un altro factory method per le configurazioni che a sua volta deve accettare in input i parametri necessari. E di nuovo si presenta il problema iniziale: come passare tutti i parametri necessari?

shodan
23-04-2010, 17:51
Dipende anche da come hai strutturato la cosa.
Puoi preparare le varie configurazioni, inserirle in una mappa con chiave identica a quella che usi per il factory e passare una mappa di configurazioni. All'interno del factory, sceglierai quella corretta da passare al costruttore della classe concreta.
Oppure puoi creare la tua configurazione un attimo prima di chiamare il factory e passarla al momento. Non mi pare necessario creare una factory di configurazioni.

mciasco
23-04-2010, 19:02
Forse mi sfugge qualcosa pero', il problema dell'esempio e' che ognuna delle classi derivate da Drink e' "complessa" abbastanza da richiedere una propria configurazione e quindi in pratica deve esistere una gerarchia di classi parallela in cui ci saranno le classi-configurazione WineConfiguration, BeerConfiguration...

Se non si usasse questa seconda gerarchia di classi, si perderebbe il parallelismo fra le classi derivate da Drink e le classi di configurazione per ognuna di esse.
E' un parallelismo implicito ok, nel senso che se aggiungo una classe Water derivata da Drink, nessun mi avvisa automaticamente della necessita' di una classe WaterConfiguration.

La necessita' di avere una factory anche per le configurazione e' proprio per formalizzare questo parallelismo, e ovviamente anche per automatizzare la creazione e configurazione di queste maledette classi derivate da Drink. Infatti sarebbe bello che con un un'unico factory method si potesse istanziare una classe-configurazione e una classe-drink inizializzata in base alla configurazione... mmm, questo mi fa pensare piu' ad una Abstract Factory... :master:

mciasco
23-04-2010, 19:25
C'ho pensato e no, non funziona nemmeno l'Abstract Factory.
No decisamente il problema e' che ad un certo punto occorre istanziare una classe-configurazione, e i parametri da passare al costruttore sono noti solo al programmatore stesso che scrive il codice.
Quindi in effetti sembra che l'unica cosa possibile sia chiamare esplicitamente il costruttore di una configurazione nel codice e passare l'oggetto creato al factory method che creera' l'istanza della classe-drink concreta.
:incupito: :bh:

giuseppe500
27-04-2010, 00:11
Cioe' se ho le classi concrete Wine e Beer che derivano dalla classe base Drink, il factory method in questione sara' ad esempio "Drink createDrink(string drinkType, DrinkConfiguration config)".
Ovviamente questo significa che si avranno anche le due classi concrete WineConfiguration e BeerConfiguration che derivano da DrinkConfiguration.


allora:
se il problema sono i parametri
forse questo potrebbe interessarti:



/* The program below illustrates passing a variable
* number of arguments using the following macros:
* va_start va_arg va_end
* va_list va_dcl (UNIX only)
*/

#include <stdio.h>
#define ANSI /* Comment out for UNIX version */
#ifdef ANSI /* ANSI compatible version */
#include <stdarg.h>
int average( int first, ... );
#else /* UNIX compatible version */
#include <varargs.h>
int average( va_list );
#endif

int main( void )
{
/* Call with 3 integers (-1 is used as terminator). */
printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );

/* Call with 4 integers. */
printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );

/* Call with just -1 terminator. */
printf( "Average is: %d\n", average( -1 ) );
}

/* Returns the average of a variable list of integers. */
#ifdef ANSI /* ANSI compatible version */
int average( int first, ... )
{
int count = 0, sum = 0, i = first;
va_list marker;

va_start( marker, first ); /* Initialize variable arguments. */
while( i != -1 )
{
sum += i;
count++;
i = va_arg( marker, int);
}
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}
#else /* UNIX compatible version must use old-style definition. */
int average( va_alist )
va_dcl
{
int i, count, sum;
va_list marker;

va_start( marker ); /* Initialize variable arguments. */
for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ )
sum += i;
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}
#endif



e questo post:

http://www.daniweb.com/forums/thread113972.html#

i 3 punti sono un metodo per passare n parametri ad una funzione un po come il printf, solo che secondo me non una gran soluzione perch non so se si puo' utilizzare per metodi virtuali.

Io poi utilizzerei un abstract factory o un builder passando come parametro al metodo una classe che deriva da un interfaccia comune ma fa l'override dei metodi virtuali della classe base magari coi 3 punti costruendo tutto cio' che ti occorre in modo polimorfo .
sempre se possibile fare l'override dei 3 punti.

A me pero' sembra un gran casino e il gioco non vale la candela.
scusa se ti ho fatto perdere tempo.

shodan
27-04-2010, 13:18
Io sconsiglio le variadic function. Sono lente (tocca usare le varie macro) e non sono safe per i tipo non POD (eventuali costruttori di copia non sono chiamati).

Piuttosto cambierei approccio e mischierei due pattern: il comman e il factory.
La chiave non pi un parametro esterno, ma la classe di configurazione stessa che viene passata al factory vero, ma sar la classe di configurazione a creare l'oggetto vero.



class Drink;
struct DrinkConfiguration {
Drink* create()=0;
~DrinkConfiguration() {}
};

class WineConfiguration : public DrinkConfiguration {
public:
WineConfiguration (const int a, const int b) : a_(a), b_(b) {}
Drink* create() { return new WineDrink(a,b); }
~WineConfiguration () {}
private:
int a_;
int b_;
};


class BeerConfiguration : public DrinkConfiguration {
public:
BeerConfiguration (const string a, const string b) : a_(a), b_(b) {}
Drink* create() { return new BeerDrink(a,b); }
~BeerConfiguration () {}
private:
string a_;
string b_;
};

Drink* Factoy::CreateDrink(DrinkConfiguration& drinkConfig) {
return drinkConfig.create();
}


Drink* myDrink = Factoy::CreateDrink(WineConfiguration(1,10));
Drink* yourDrink = Factoy::CreateDrink(BeerConfiguration("birra","vattelapesca"));

Loading