Domanda

Non so se esiste un nome ufficiale per questo, ma ho giocato con quello che mi piace chiamare "auto-fabbrica" modello. Fondamentalmente, è quando una classe base astratta funge da fabbrica per se stessa. Lasciami spiegare:

Ho oggetti Foo e Bar nel mio sistema, che sono usati tramite le interfacce FooInterface e BarInterface. Devo dare ai miei clienti il ??giusto tipo di Foo e Bar. La decisione su quale oggetto Foo concreto creare è presa al momento della compilazione. Ad esempio, se si compila su win32, si desidera creare solo oggetti Win32Foo e se si compila su OSX si desidera creare solo oggetti OSXFoo e così via. Tuttavia, la decisione di quale oggetto Bar concreto creare è presa in fase di esecuzione, sulla base di una stringa chiave.

Ora, la mia domanda riguarda il modo migliore per implementare questo schema. Un modo in cui mi viene in mente utilizza fabbriche regolari:

shared_ptr<FooInterface> foo = FooFactory::create();
shared_ptr<BarInterface> happyBar = BarFactory::create("Happy");
shared_ptr<BarInterface> sadBar = BarFactory::create("Sad");

Un altro modo è quello di usare ciò che chiamo "fabbriche automatiche":

shared_ptr<FooInterface> foo = FooInterface::create();
shared_ptr<BarInterface> happyBar = BarInterface::create("Happy");
shared_ptr<BarInterface> sadBar = BarInterface::create("Sad");

Quali sono i pro e i contro di ogni approccio, sia dal punto di vista dell'usabilità che dal punto di vista architettonico?

È stato utile?

Soluzione

Le fabbriche hanno due usi comuni:

1) Decidi il tipo polimorfico dinamico in fase di esecuzione, in base a parametri e / o stato globale (come la configurazione). Il tuo modello fa questo.

2) Iniezione delle dipendenze: anziché utilizzare una funzione statica per creare oggetti, utilizzare un oggetto di fabbrica, in modo che il tipo di oggetto restituito possa essere configurato dal chiamante, passando in qualsiasi fabbrica volere. Nessuno di questi schemi fornisce questo. Inoltre, il tuo secondo modello non consente nemmeno l'iniezione di dipendenza statica (avendo funzioni template che prendono una classe factory come parametro template), perché l'interfaccia e la factory sono le stesse.

Quindi uno svantaggio del tuo modello (e delle tue fabbriche regolari) è che l'iniezione di dipendenza non è realmente supportata. C'è una e una sola funzione da chiamare per ottenere un oggetto che è FooInterface e che è FooInterface :: create (). Non sosterrò perché l'iniezione di dipendenza sia utile, sottolineo solo che se costruisci in questo modo, non puoi usarla.

Altri suggerimenti

Farei un miglioramento:

shared_ptr<FooInterface> foo = Factory<FooInterface>::create();
shared_ptr<BarInterface> happyBar = Factory<BarInterface>::create("Happy");
shared_ptr<BarInterface> sadBar = Factory<BarInterface>::create("Sad");

Dichiareresti:

template <class I>
struct Factory { };

E poi per ogni interfaccia che necessita di una fabbrica, dovresti farlo:

template <>
struct Factory<FooInterface>
{
    static FooInterface create();
};

Ciò consente di mantenere l'implementazione di fabbrica separata dalla dichiarazione dell'interfaccia, ma utilizzando comunque il sistema dei tipi per vincolarli al momento della compilazione.

Di solito le fabbriche sono responsabili della creazione di oggetti di intere gerarchie di classi. Quindi nel tuo esempio avresti una Win32Factory, una OSXFactory ecc. Un vantaggio di questo è che devi selezionare l'implementazione specifica (win32 / unix / etc) una sola volta - durante la creazione di fabbrica, ma se usi interfacce di classe, hai per fornire continuamente le informazioni del sistema operativo.

Se hai solo due classi (Foo e Bar) non ne sono sicuro, se ne vale la pena creare fabbriche per loro e non usare solo un metodo create delle interfacce.

Oh, e quando un'interfaccia ha un metodo per creare oggetti del suo tipo, si chiama metodo di fabbrica modello .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top