Verstößt das Factory-Muster gegen das Open / Closed-Prinzip?

13

Warum verwendet ShapeFactory bedingte Anweisungen, um zu bestimmen, welches Objekt instanziiert werden soll. Müssen wir ShapeFactory nicht ändern, wenn wir in Zukunft weitere Klassen hinzufügen möchten? Warum verstößt dies nicht gegen das Open-Closed-Prinzip?

Fabrik-Muster-Design

ShapeFactory Design

Armon Safai
quelle
2
Auf welches „Fabrikmuster“ beziehen Sie sich genau? Im Allgemeinen ist eine Fabrik ein Objekt oder eine Methode, die zur Instanziierung eines Objekts dient. Dann gibt es spezielle Variationen dieser allgemeinen Idee, wie beispielsweise das abstrakte Factory-Muster, bei dem jede Factory-Instanz eine bestimmte Palette von Auswahlmöglichkeiten darstellt - in der Regel über Unterklassen und nicht über Bedingungen.
amon
3
Danke für diese Info, das klärt viel auf. Dies ist definitiv ein Beispiel für ein Fabrikmuster, aber nicht für das abstrakte Fabrikmuster, das üblicherweise mit Fabrikmustern assoziiert wird. Der Code in diesem Artikel ist ziemlich fragwürdig, und ich würde nicht erwarten, so etwas in echtem Code zu sehen.
amon
@ArmonSafai: Du verlinkst diesen Blog-Beitrag oft, aber du erklärst nicht wirklich warum. Kennen wir das Muster nicht? Wir haben auch Google, genau wie Sie.
Robert Harvey
1
@RobertHarvey Ich verknüpfe diesen Blog-Beitrag, um zu zeigen, wie das Fabrikmuster auf dieser Seite Bedingungen verwendet
Armon Safai

Antworten:

20

Die konventionelle objektorientierte Weisheit besteht darin, ifAnweisungen zu vermeiden und sie durch den dynamischen Versand überschriebener Methoden in Unterklassen einer abstrakten Klasse zu ersetzen. So weit, ist es gut.

Der Sinn des Factory-Musters besteht jedoch darin , dass Sie nicht mehr über die einzelnen Unterklassen Bescheid wissen müssen und nur noch mit der abstrakten Oberklasse arbeiten müssen . Die Idee ist, dass die Factory besser als Sie weiß, welche bestimmte Klasse instanziiert werden soll, und dass Sie nur mit den Methoden arbeiten sollten, die von der Superklasse veröffentlicht wurden. Dies ist oft wahr und ein wertvolles Muster.

Daher kann das Schreiben einer Factory-Klasse auf keinen Fall auf die ifAnweisungen verzichten . Es würde die Last der Auswahl einer bestimmten Klasse auf den Anrufer verlagern, genau das soll das Muster vermeiden. Nicht alle Prinzipien sind absolut (in der Tat ist kein Prinzip absolut), und wenn Sie dieses Muster verwenden, gehen Sie davon aus, dass der Nutzen davon größer ist als der Nutzen, wenn Sie kein verwenden if.

Kilian Foth
quelle
2
Es ist durchaus möglich, ein Factory-Muster ohne viele ifs zu erstellen . In der Antwort von @ BЈовић finden Sie ein einfaches Beispiel, wie dies erreicht werden kann. Abgestimmt.
David Arno
11
@DavidArno Natürlich gibt es verschiedene Möglichkeiten, die konkrete Klasse auszuwählen. Service Locator ist einer, ein konfigurierbarer IoC Container ist ein anderer. Dies sind nur Implementierungsdetails. Sie lenken nicht von Killians Hauptbotschaft ab, nämlich, dass die Factory den Anrufer von der Entscheidung entbindet, welche konkrete Klasse instanziiert werden soll. Verirren Sie sich nicht in den Details.
Robert Harvey
1
Tolle Aussage, die die Frage in keiner Weise beantwortet.
Martin Maat
1
@ R.Schmitz Ich denke du bist falsch in deiner Annahme. Ich denke, viele Leute haben diese Frage aus dem OP verpasst: "Müssen wir ShapeFactory nicht modifizieren, wenn wir in Zukunft weitere Klassen hinzufügen möchten?" Klar, das OP ist verwirrt, wenn man bedenkt, dass dieses Muster gegen OCP verstößt, weil man vorhandenen Code ändern muss, um neue Funktionen hinzuzufügen. Die richtige Antwort auf diese Frage finden Sie in meiner Antwort. Die kurze Antwort: Sie lassen diesen Code in Ruhe und wenden Abstract Factory-Muster an, um Ihre vorhandene Funktionalität zu ERWEITERN (nicht zu ändern). Aus diesem Grund geht Kilians Antwort NICHT auf die Frage ein.
Hfontanez
4

Im Beispiel wird wahrscheinlich eine bedingte Anweisung verwendet, da dies die einfachste ist. Eine komplexere Implementierung könnte eine Map oder Konfiguration oder (wenn Sie wirklich ausgefallen sein möchten) eine Art Registrierung verwenden, in der sich die Klassen selbst registrieren können. Es ist jedoch nichts Falsches daran, eine Bedingung zu verwenden, wenn die Anzahl der Klassen gering ist und sich selten ändert.

Die Ausweitung der Bedingung auf die Unterstützung einer neuen Unterklasse in der Zukunft wäre in der Tat eine Verletzung des offenen / geschlossenen Prinzips. Die "richtige" Lösung wäre, eine neue Fabrik mit der gleichen Schnittstelle zu erstellen. Die Einhaltung des O / C-Prinzips sollte jedoch immer gegen andere Designprinzipien wie KISS und YAGNI abgewogen werden.

Der angezeigte Code ist jedoch eindeutig ein Beispielcode, der das Konzept einer Fabrik und nichts anderes darstellen soll. ZB ist es ein schlechter Stil, wie im Beispiel null zurückzugeben, aber eine aufwändigere Fehlerbehandlung würde den Punkt nur verdunkeln. Der Beispielcode ist kein Code für die Produktionsqualität.

JacquesB
quelle
Können Sie bitte erklären, wie eine Karte / Konfiguration / Registrierung funktionieren würde?
Armon Safai
@ArmonSafai: Hier ein Beispiel: jkfill.com/2010/12/29/self-registering-factories-in-c-sharp
JacquesB
Selbstregistrierende Fabriken, AFAIK, sind in C ++ in statischen Bibliotheken nicht möglich, da nicht verwendete (dh nicht verwendete) globale Variablen von Toolchains verworfen werden.
void.pointer
@ArmonSafai lesen Sie dies für mehr Verständnis goo.gl/RYuNSM
AZ_
2

Das Muster selbst verstößt nicht gegen das Open / Closed-Prinzip (OCP). Wir verletzen jedoch die OCP, wenn wir das Muster falsch verwenden.

Die einfache Antwort auf diese Frage lautet wie folgt:

  1. Erstellen Sie Ihre Basisfunktionalität mit Factory Method Pattern.
  2. ERWEITERN Sie Ihre Funktionalität mit dem Abstract Factory Pattern

In dem bereitgestellten Beispiel unterstützt Ihre Basisfunktionalität drei Formen: Kreis, Rechteck und Quadrat. Angenommen, Sie müssen Triangle, Pentagon und Hexagon in Zukunft unterstützen. Um dies OHNE Verletzung von OCP zu tun , müssen Sie eine zusätzliche Factory erstellen, um Ihre neuen Shapes zu unterstützen (wir rufen sie auf AdvancedShapeFactory), und dann mit AbstractFactory entscheiden, welche Factory Sie erstellen müssen, um welche Shapes Sie benötigen.

hfontanez
quelle
Ich bevorzuge die selbstregistrierende Lösung für Fabriken (in Ermangelung eines wirklich konfigurierbaren IoC-Containers, der am besten geeignet ist), da wir ansonsten aus Ihrem Vorschlag im Grunde Fabriken von Fabriken erhalten , und da werden die Dinge einfach zu komplex.
Nom1fan
1

Wenn Sie über das Abstract Factory-Muster sprechen, liegt die Entscheidungsfindung häufig nicht in der Factory selbst, sondern im Anwendungscode. Dieser Code wählt aus, welche konkrete Factory instanziiert und an den Clientcode übergeben werden soll, der von der Factory erzeugte Objekte verwendet. Das Ende des Java-Beispiels finden Sie hier: https://en.wikipedia.org/wiki/Abstract_factory_pattern

Entscheidungsfindung impliziert nicht unbedingt ifAussagen. Es könnte den konkreten Factory-Typ aus einer Konfigurationsdatei lesen, ihn aus einer Kartenstruktur ableiten usw.

guillaume31
quelle
Wenn ich als Anrufer die Entscheidung treffe, welche konkrete Klasse instanziiert werden soll, warum beschäftige ich mich dann mit einer abstrakten Fabrik?
Robert Harvey
Bitte definieren Sie "den Anrufer". Wie ich in meiner Antwort beschreibe, gibt es den globalen Anwendungscode und dann den Code, der zum Erstellen von Objekten mithilfe einer Factory erforderlich ist. Während letztere in der Tat die konkrete Klasse nicht kennen müssen, um zu instanziieren, muss ein anderer Kontextcode darüber Bescheid wissen und sie neu
aufsetzen
0

Wenn Sie über das Öffnen-Schließen auf Klassenebene mit dieser Factory nachdenken, erstellen Sie eine andere Klasse in Ihrem System. Öffnen-Schließen. Wenn Sie beispielsweise eine andere Klasse haben, die eine Form annimmt und die Fläche berechnet (typisches Beispiel), ist diese Klasse OpenClose, weil Es kann die Fläche für neue Arten von Formen ohne Änderung berechnen. Dann haben Sie eine andere Klasse, die die Form zeichnet, eine andere Klasse, die N Formen annimmt und die größere zurückgibt, und Sie können allgemein annehmen, dass die anderen Klassen in Ihrem System, die sich mit Formen befassen, Open-Close sind (zumindest über Formen). In Bezug auf das Design ermöglicht die Fabrik, dass der Rest des Systems offen und geschlossen ist, und die Fabrik selbst ist NICHT offen und geschlossen.

Natürlich können Sie diese Fabrik auch durch eine Art dynamisches Laden auf- und zuklappen lassen und Ihr gesamtes System kann auf- und zuklappen (Sie können beispielsweise neue Formen hinzufügen, indem Sie ein Glas in den Klassenpfad legen). Sie müssen bewerten, ob sich diese zusätzliche Komplexität je nach dem System lohnt, das Sie erstellen. Nicht alle Systeme benötigen steckbare Funktionen und nicht alle Systeme müssen vollständig geöffnet und geschlossen sein.

AlfredoCasado
quelle
0

Das Open-Closed-Prinzip gilt als Liskov-Substitutionsprinzip für Klassenbäume und Vererbungshierarchien. In Ihrem Beispiel befindet sich die Factory-Klasse nicht im Stammbaum der Klassen, die sie instanziiert, sodass diese Regeln nicht verletzt werden können. Es würde eine Verletzung geben, wenn Ihr GetShape (oder besser CreateShape) in der Shape-Basisklasse implementiert würde.

Martin Maat
quelle
-2

Es hängt alles davon ab, wie Sie es implementieren. Mit können Sie std::mapFunktionszeiger auf Funktionen speichern, die Objekte erstellen. Dann wird das Auf / Zu-Prinzip nicht verletzt. Oder Schalter / Koffer.

Auf jeden Fall können Sie die Abhängigkeitsinjektion immer verwenden, wenn Ihnen das Factory-Muster nicht gefällt.

BЈовић
quelle
6
Wie ist Schalter / Fall besser als Bedingungen? Die Verwendung einer Map / eines Dict / einer Tabelle zur Darstellung von Code als Daten ist gut, wenn Sie tatsächlich eine Registrierung für verschiedene Implementierungen benötigen - z. B. in einigen DI-Container-Implementierungen. Für die meisten Fabriken ist es jedoch nicht erforderlich, unterschiedliche Rückrufe desselben Typs zu haben! Ich verstehe nicht ganz, warum Sie das vorschlagen. Außerdem sind viele DI-Container in Bezug auf Factory-Objekte implementiert, sodass es ein bisschen kreisförmig erscheint, DI anstelle von Factorys zu verwenden.
amon
1
@amon Ich wollte andere DI-Typen verwenden - nicht die Fabrik.
BЈовић
1
Wie wird Ihre Fabrik entscheiden, welcher Zeiger verwendet werden soll? Irgendwann muss man sich entscheiden.
Whatsisname
@ArmonSafai ???
Freitag,