2009-06-24 5 views
3

Ich weiß nicht, ob es einen offiziellen Namen dafür gibt, aber ich habe mit dem gespielt, was ich gerne das "Self-Factory" -Muster nenne. Im Grunde ist es, wenn eine abstrakte Basisklasse als eine Fabrik für sich selbst agiert. Lassen Sie mich erklären:Das Muster "Self-Factory"

Ich habe Foo-Objekte und Bar-Objekte in meinem System, die über Schnittstellen FooInterface und BarInterface verwendet werden. Ich muss meinen Kunden die richtige Art von Foo und Bar geben. Die Entscheidung, welches konkrete Foo-Objekt erstellt werden soll, wird zur Kompilierzeit getroffen. Wenn Sie beispielsweise unter Win32 kompilieren, möchten Sie nur Win32Foo-Objekte erstellen. Wenn Sie unter OSX kompilieren, möchten Sie nur OSXFoo-Objekte usw. erstellen. Die Entscheidung, welches konkrete Bar-Objekt erstellt werden soll, wird jedoch zur Laufzeit basierend auf einer Schlüsselzeichenfolge getroffen.

Jetzt ist meine Frage über die beste Möglichkeit, dieses Schema zu implementieren. Ein Weg, ich komme mit verwendet reguläre Fabriken:

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

Ein anderer Weg ist zu verwenden, was ich als „self-Fabriken“:

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

Was sind die Vor- und Nachteile der einzelnen Ansätze, die beide aus ein Usability-Standpunkt und von einem architektonischen Standpunkt?

+0

Ich denke, was Sie als Self-Factory vorschlagen, wird auch das Prototype-Muster genannt. Sie können wahrscheinlich Literatur über dieses Muster für Ideen überprüfen. –

Antwort

2

Fabriken haben zwei gemeinsame Verwendungen:

1) zur Laufzeit dynamisch Entscheiden polymorphen Typ, auf Basis von Parametern und/oder globalen Zustand (wie beispielsweise Konfiguration). Dein Muster macht das.

2) Injektion Abhängigkeit: anstatt eine statische Funktion Objekte zu erstellen, verwenden Sie eine Fabrik Objekt, so dass die Art des Objekts kann vom Anrufer konfiguriert werden zurückgegeben, im Vorbeigehen, was auch immer sie wollen Fabrik. Keines dieser Muster bietet dies. Darüber hinaus erlaubt das zweite Muster keine statische Abhängigkeitsinjektion (indem Vorlagenfunktionen verwendet werden, die eine Factory-Klasse als Vorlagenparameter verwenden), da die Schnittstelle und die Factory identisch sind.

Also eine Con von Ihrem Muster (und von Ihren regulären Fabriken) ist, dass Abhängigkeitsinjektion nicht wirklich unterstützt wird. Es gibt eine und nur eine Funktion, die aufgerufen wird, um ein Objekt zu erhalten, das ein FooInterface ist, und das ist FooInterface :: create(). Ich werde nicht argumentieren, warum Dependency-Injection nützlich ist, weisen Sie einfach darauf hin, dass Sie diese Methode nicht verwenden können, wenn Sie auf diese Weise bauen.

1

Üblicherweise sind Factories für die Erstellung von Objekten ganzer Klassenhierarchien zuständig. In Ihrem Beispiel hätten Sie also eine Win32Factory, OSXFactory usw. Ein Vorteil davon ist, dass Sie die spezifische Implementierung (win32/unix/etc) nur einmal auswählen müssen - während der Factory-Erstellung, aber wenn Sie Klassenschnittstellen verwenden um die OS Informationen die ganze Zeit zu liefern.

Wenn Sie nur zwei Klassen (Foo und Bar) haben, bin ich mir nicht sicher, ob es sich lohnt, Fabriken für sie zu erstellen und nicht nur eine create Methode der Schnittstellen zu verwenden.

Oh, und wenn eine Schnittstelle eine Methode zum Erstellen von Objekten des Typs hat, heißt es factory method pattern.

3

ich eine Verbesserung machen würde:

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

Sie würden erklären:

template <class I> 
struct Factory { }; 

Und dann für jede Schnittstelle, die eine Fabrik braucht, dann würden Sie dies tun:

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

Damit können Sie die Factory-Implementierung von der Schnittstellendeklaration getrennt halten, aber immer noch das Typsystem zu bi nd sie zur Kompilierzeit.