Factory-Pattern (oder zumindest die Verwendung von FactoryFactory..
) ist der Kern vieler Witze, wie hier .
Abgesehen von ausführlichen und "kreativen" Namen wie " RequestProcessorFactoryFactory.RequestProcessorFactory" ist das Factory-Muster grundlegend falsch, wenn Sie in Java / C ++ programmieren müssen und es einen Verwendungszweck für " Abstract_factory_pattern" gibt .
Wie würde eine andere beliebte Sprache (zum Beispiel Ruby oder Scala ) es vermeiden, sie zu verwenden, während sie eine ähnliche Komplexität bewältigt?
Der Grund, den ich frage, ist, dass ich hauptsächlich die Kritik an Fabriken sehe, die im Kontext des Java / Java EE- Ökosystems erwähnt wurden, aber sie erklären nie, wie andere Sprachen / Frameworks sie lösen.
java
design-patterns
senseiwu
quelle
quelle
Antworten:
Ihre Frage ist mit "Java" markiert. Kein Wunder, dass Sie sich fragen, warum das Factory-Muster verspottet wird: Java selbst wird mit einem gut verpackten Missbrauch dieses Musters ausgeliefert.
Versuchen Sie beispielsweise, ein XML-Dokument aus einer Datei zu laden und eine XPath-Abfrage für diese Datei auszuführen. Sie benötigen etwa 10 Codezeilen, um die Factories und Builder einzurichten:
Ich frage mich, ob die Leute, die diese API entworfen haben, jemals als Entwickler gearbeitet haben oder nur Bücher gelesen und Dinge herumgeworfen haben. Ich verstehe, dass sie selbst keinen Parser schreiben wollten und die Aufgabe anderen überließen, aber es ist trotzdem eine hässliche Implementierung.
Da Sie sich fragen, was die Alternative ist, laden Sie hier die XML-Datei in C #:
Ich vermute, neu geschlüpfte Java-Entwickler sehen den Factory-Wahnsinn und denken, dass es in Ordnung ist, das zu tun - wenn die Genies, die Java selbst gebaut haben, sie so oft benutzt haben.
Factory und alle anderen Muster sind Werkzeuge, die jeweils für eine bestimmte Aufgabe geeignet sind. Wenn Sie sie auf Jobs anwenden, sind sie nicht dafür geeignet, hässlichen Code zu haben.
quelle
XmlDocument xml = new XmlDocument(); xml.Load("c:\\employees.xml"); XmlNodeList nodes = xml.SelectNodes(expression);
(Insgesamt ist LINQ to XML viel einfacher zu verwenden, wenn auch hauptsächlich in anderen Bereichen.) Und hier ist ein KB-Artikel Ich fand mit einer von MS empfohlenen Methode: support.microsoft.com/kb/308333Wie so oft missverstehen die Leute, was los ist (und das schließt viele der Lacher ein).
Es ist nicht das Fabrikmuster an sich, das so schlecht ist wie die Art und Weise, wie viele (vielleicht sogar die meisten) Leute es benutzen.
Und das ergibt sich zweifellos aus der Art und Weise, wie Programmieren (und Muster) gelehrt wird. Schulkinder (die sich oft als "Schüler" bezeichnen) werden angewiesen, "X mit dem Muster Y zu erstellen". Nach einigen Iterationen wird davon ausgegangen, dass dies der Weg ist, um Programmierprobleme anzugehen.
Also wenden sie ein bestimmtes Muster an, das ihnen in der Schule gefällt, und zwar gegen alles und gegen alles, ob es angemessen ist oder nicht.
Und dazu gehören auch Universitätsprofessoren, die leider Bücher über Softwaredesign schreiben.
Der Höhepunkt war ein System, das ich ausgesprochen unangenehm pflegen musste (der Mann besaß sogar mehrere Bücher über objektorientiertes Design und eine Lehrtätigkeit in der CS-Abteilung einer großen Universität) ).
Es basierte auf einem 3-Tier-Muster, wobei jedes Tier selbst ein 3-Tier-System war (muss entkoppelt werden ...). Auf der Schnittstelle zwischen jedem Satz von Schichten befand sich auf beiden Seiten eine Fabrik, um die Objekte zu produzieren, um Daten an die andere Schicht zu übertragen, und eine Fabrik, um das Objekt zu produzieren, um das empfangene Objekt in eines zu übersetzen, das zur empfangenden Schicht gehört.
Für jede Factory gab es eine abstrakte Factory (wer weiß, die Factory muss sich möglicherweise ändern und dann soll sich der aufrufende Code nicht ändern ...).
Und das ganze Durcheinander war natürlich völlig undokumentiert.
Das System hatte eine Datenbank, die auf die 5. Normalform normalisiert war (ich mache mir nichts vor).
Ein System, das im Wesentlichen nur ein Adressbuch war, mit dem eingehende und ausgehende Dokumente protokolliert und verfolgt werden konnten. Es verfügte über eine Codebasis von 100 MB in C ++ und über 50 Datenbanktabellen. Das Drucken von 500 Serienbriefen dauert bis zu 72 Stunden, wenn ein Drucker verwendet wird, der direkt an den Computer angeschlossen ist, auf dem die Software ausgeführt wird.
Das ist der Grund, warum sich die Leute über Muster lustig machen und sich speziell auf ein bestimmtes Muster konzentrieren.
quelle
Fabriken haben viele Vorteile, die in manchen Situationen ein elegantes Anwendungsdesign ermöglichen. Zum einen können Sie die Eigenschaften von Objekten, die Sie später erstellen möchten, an einem Ort festlegen, indem Sie eine Factory erstellen und diese Factory dann übergeben. Aber oft muss man das gar nicht machen. In diesem Fall erhöht die Verwendung einer Factory lediglich die Komplexität, ohne dass Sie tatsächlich eine Gegenleistung erhalten. Nehmen wir zum Beispiel diese Fabrik:
Eine Alternative zum Factory-Muster ist das sehr ähnliche Builder-Muster. Der Hauptunterschied besteht darin, dass die Eigenschaften der von einer Factory erstellten Objekte beim Initialisieren der Factory festgelegt werden, während ein Builder mit einem Standardstatus initialisiert wird und anschließend alle Eigenschaften festgelegt werden.
Wenn Sie jedoch ein Problem mit der Überentwicklung haben, ist das Ersetzen einer Factory durch einen Builder wahrscheinlich keine große Verbesserung.
Der einfachste Ersatz für eines der Muster ist natürlich das Erstellen von Objektinstanzen mit einem einfachen Konstruktor mit dem
new
Operator:Konstruktoren haben jedoch in den meisten objektorientierten Sprachen einen entscheidenden Nachteil: Sie müssen ein Objekt dieser genauen Klasse zurückgeben und können keinen Untertyp zurückgeben.
Wenn Sie den Subtyp zur Laufzeit auswählen müssen, aber dafür keine neue Builder- oder Factory-Klasse erstellen möchten, können Sie stattdessen eine Factory-Methode verwenden. Dies ist eine statische Methode einer Klasse, die neue Instanzen dieser Klasse oder einer ihrer Unterklassen zurückgibt. Eine Factory, die keinen internen Zustand beibehält, kann häufig durch eine solche Factory-Methode ersetzt werden:
Eine neue Funktion in Java 8 sind Methodenreferenzen, mit denen Sie Methoden weitergeben können, genau wie Sie es mit einer zustandslosen Factory tun würden. Praktischerweise akzeptiert jede Methode, die eine Methodenreferenz akzeptiert, auch alle Objekte, die dieselbe Funktionsschnittstelle implementieren. Dies kann auch eine vollwertige Factory mit internem Status sein, sodass Sie Fabriken später problemlos einführen können, wenn Sie einen Grund dafür sehen.
quelle
foo = new Bar(23);
gleichwertig zu seinfoo = Bar._createInstance(23);
. Ein Objekt sollte in der Lage sein, seinen eigenen Typ zuverlässig mit einer privatengetRealType()
Methode abzufragen , aber Objekte sollten in der Lage sein, einen Supertyp zu bestimmen, der von externen Aufrufen an zurückgegeben werden sollgetType()
. Externer Code sollte sich nicht darum kümmern, ob ein Aufruf vonnew String("A")
tatsächlich eine Instanz vonString
[im Gegensatz zu z. B. einer Instanz vonSingleCharacterString
] zurückgibt .draw(OutputStream out)
etwas anderes HTML enthalten, aber generieren. Die statische Methodenfactory erstellt die richtige Klasse für die Situation und dann kann der Aufrufer einfach die Zeichenmethode verwenden.[[SomeClass alloc] init]
. Es ist möglich, eine völlig andere Klasseninstanz oder ein anderes Objekt (z. B. einen zwischengespeicherten Wert) zurückzugebenFabriken der einen oder anderen Art finden sich unter geeigneten Umständen in so gut wie jeder objektorientierten Sprache. Manchmal müssen Sie einfach nur eine Möglichkeit auswählen, welche Art von Objekt basierend auf einem einfachen Parameter wie einer Zeichenfolge erstellt werden soll.
Einige Leute gehen zu weit und versuchen, ihren Code so zu entwickeln, dass sie nur innerhalb einer Factory einen Konstruktor aufrufen müssen. Es wird langsam lächerlich, wenn man Fabrikfabriken hat.
Was mich beeindruckt hat, als ich Scala lernte und Ruby nicht kenne, ist, dass die Sprache so aussagekräftig ist, dass Programmierer nicht immer versuchen, die "Klempnerarbeit" auf externe Konfigurationsdateien zu übertragen . Anstatt versucht zu sein, Factory-Fabriken zu erstellen, um Ihre Klassen in verschiedenen Konfigurationen miteinander zu verbinden, verwenden Sie in Scala Objekte mit eingemischten Merkmalen. Es ist auch relativ einfach, einfache DSLs in der Sprache für Aufgaben zu erstellen, die in Java häufig stark überarbeitet sind.
Wie auch andere Kommentare und Antworten gezeigt haben, machen Verschlüsse und erstklassige Funktionen viele Muster überflüssig. Aus diesem Grund glaube ich, dass viele der Anti-Patterns mit zunehmender Verbreitung von C ++ 11 und Java 8 verschwinden werden.
quelle
Im Allgemeinen gibt es diesen Trend, dass Java-Programme fürchterlich überentwickelt sind. Viele Fabriken zu haben, ist eines der häufigsten Symptome von Überentwicklung. Deshalb machen sich die Leute über diese lustig.
Insbesondere besteht das Problem, das Java bei Fabriken hat, darin, dass in Java a) Konstruktoren keine Funktionen sind und b) Funktionen keine erstklassigen Bürger sind.
Stellen Sie sich vor, Sie könnten so etwas schreiben (seien Sie
Function
eine bekannte Schnittstelle der JRE )Siehe, keine Factory-Interfaces oder Klassen. Viele dynamische Sprachen haben erstklassige Funktionen. Leider ist dies mit Java 7 oder früher nicht möglich (die Implementierung derselben mit Fabriken bleibt dem Leser als Übung überlassen).
Die Alternative ist also nicht so sehr "ein anderes Muster verwenden", sondern vielmehr "eine Sprache mit einem besseren Objektmodell verwenden" oder "einfache Probleme nicht überentwickeln".
quelle