Warum sollte ich eine Factory-Klasse anstelle der direkten Objektkonstruktion verwenden?

162

Ich habe die Geschichte mehrerer C # - und Java-Klassenbibliotheksprojekte auf GitHub und CodePlex gesehen und sehe einen Trend zum Umstieg auf Factory-Klassen im Gegensatz zur direkten Objektinstanziierung.

Warum sollte ich viele Factory-Klassen verwenden? Ich habe eine ziemlich gute Bibliothek, in der Objekte auf altmodische Weise erstellt werden - durch Aufruf öffentlicher Konstruktoren von Klassen. In den letzten Commits haben die Autoren schnell alle öffentlichen Konstruktoren von Tausenden von Klassen in interne geändert und außerdem eine große Factory-Klasse mit Tausenden von CreateXXXstatischen Methoden erstellt, die nur neue Objekte zurückgeben, indem sie die internen Konstruktoren der Klassen aufrufen. Die externe Projekt-API ist fehlerhaft, gut gemacht.

Warum wäre eine solche Änderung sinnvoll? Was bringt es, auf diese Weise umzugestalten? Welche Vorteile bietet es, Aufrufe an Konstruktoren öffentlicher Klassen durch statische Factory-Methodenaufrufe zu ersetzen?

Wann sollte ich öffentliche Konstruktoren verwenden und wann sollten Fabriken verwendet werden?

Rufanov
quelle
1
Was ist eine Stoffklasse / -methode?
Doval

Antworten:

56

Factory-Klassen werden häufig implementiert, weil sie es dem Projekt ermöglichen, die SOLID- Prinzipien genauer zu befolgen . Insbesondere die Prinzipien der Grenzflächensegregation und der Abhängigkeitsinversion.

Fabriken und Schnittstellen ermöglichen eine viel größere langfristige Flexibilität. Es ermöglicht ein entkoppeltes - und damit überprüfbareres - Design. Hier ist eine nicht vollständige Liste, warum Sie diesen Weg gehen könnten:

  • Hiermit können Sie auf einfache Weise einen IoC-Container ( Inversion of Control ) einführen
  • Es macht Ihren Code testbarer, da Sie Schnittstellen verspotten können
  • Es gibt Ihnen viel mehr Flexibilität, wenn Sie die Anwendung ändern müssen (dh Sie können neue Implementierungen erstellen, ohne den abhängigen Code zu ändern).

Betrachten Sie diese Situation.

Assembly A (-> bedeutet abhängig von):

Class A -> Class B
Class A -> Class C
Class B -> Class D

Ich möchte Klasse B in Assembly B verschieben, die von Assembly A abhängig ist. Mit diesen konkreten Abhängigkeiten muss ich den größten Teil meiner gesamten Klassenhierarchie verschieben. Wenn ich Schnittstellen benutze, kann ich viel Schmerz vermeiden.

Versammlung A:

Class A -> Interface IB
Class A -> Interface IC
Class B -> Interface IB
Class C -> Interface IC
Class B -> Interface ID
Class D -> Interface ID

Ich kann jetzt die Klasse B ohne jegliche Schmerzen auf die Baugruppe B verschieben. Es kommt immer noch auf die Schnittstellen in Assembly A an.

Die Verwendung eines IoC-Containers zum Auflösen Ihrer Abhängigkeiten bietet Ihnen noch mehr Flexibilität. Es ist nicht erforderlich, jeden Aufruf des Konstruktors zu aktualisieren, wenn Sie die Abhängigkeiten der Klasse ändern.

Nach dem Prinzip der Schnittstellentrennung und dem Prinzip der Abhängigkeitsinversion können hochflexible, entkoppelte Anwendungen erstellt werden. Sobald Sie an einer dieser Arten von Anwendungen gearbeitet haben, werden Sie nie mehr auf die Verwendung des newSchlüsselworts zurückgreifen wollen .

Stephen
quelle
40
IMO-Fabriken sind für SOLID am unwichtigsten. Sie können SOLID ganz gut ohne Fabriken machen.
Euphoric
26
Eine Sache, die für mich nie Sinn gemacht hat, ist, wenn man Fabriken benutzt, um neue Objekte herzustellen, muss man zuerst die Fabrik bauen. Was genau bringt dir das? Wird angenommen, dass jemand anderes Ihnen die Fabrik gibt, anstatt dass Sie sie selbst instanziieren, oder etwas anderes? Dies sollte in der Antwort erwähnt werden, andernfalls ist unklar, wie Fabriken ein Problem tatsächlich lösen.
Mehrdad
8
@ BЈовић - Mit der Ausnahme, dass Sie jetzt jedes Mal, wenn Sie eine neue Implementierung hinzufügen, die Factory aufbrechen und ändern können, um der neuen Implementierung Rechnung zu tragen - was explizit gegen OCP verstößt.
Telastyn
6
@Telastyn Ja, aber der Code, der die erstellten Objekte verwendet, ändert sich nicht. Das ist wichtiger als die Änderungen an der Fabrik.
BЈовић
8
Fabriken sind in bestimmten Bereichen nützlich, auf die sich die Antwort bezieht. Sie sind nicht überall einsetzbar . Die Verwendung von Fabriken zum Erstellen von Zeichenfolgen oder Listen würde beispielsweise zu weit führen. Auch für UDTs sind sie nicht immer notwendig. Der Schlüssel besteht darin, eine Factory zu verwenden, wenn die genaue Implementierung einer Schnittstelle entkoppelt werden muss.
126

Wie der Name schon sagt, glaube ich, dass dies ein Fall von Frachtkult- Softwaredesign ist. Factories, insbesondere die abstrakte, können nur verwendet werden, wenn Ihr Modul mehrere Instanzen einer Klasse erstellt und Sie dem Benutzer dieses Moduls die Möglichkeit geben möchten, den zu erstellenden Typ anzugeben. Diese Anforderung ist eigentlich recht selten, da Sie die meiste Zeit nur eine Instanz benötigen und diese Instanz auch direkt übergeben können, anstatt eine explizite Factory zu erstellen.

Fakt ist, dass Fabriken (und Singletons) extrem einfach zu implementieren sind und die Leute sie daher häufig verwenden, auch an Orten, an denen sie nicht benötigt werden. Also, wenn Programmierer denkt "Welche Entwurfsmuster sollte ich in diesem Code verwenden?" Die Fabrik ist die erste, die ihm in den Sinn kommt.

Viele Fabriken entstehen, weil "Vielleicht muss ich diese Klassen eines Tages anders erstellen". Welches ist eine klare Verletzung von YAGNI .

Und Fabriken werden obsolet, wenn Sie das IoC-Framework einführen, da IoC nur eine Art Fabrik ist. Viele IoC-Frameworks sind in der Lage, Implementierungen bestimmter Fabriken zu erstellen.

Außerdem gibt es kein Entwurfsmuster, das besagt, dass riesige statische Klassen mit CreateXXXMethoden erstellt werden sollen, die nur Konstruktoren aufrufen. Und es wird vor allem keine Fabrik (oder abstrakte Fabrik) genannt.

Euphorisch
quelle
6
Ich stimme mit den meisten Ihrer Punkte außer „IoC ist eine Art Fabrik“ : IoC Container sind nicht Fabriken . Sie sind eine bequeme Methode Dependency Injection zu erreichen. Ja, sind einige der Lage automatische Fabriken für den Bau, aber sie sind nicht Fabriken selbst und sollten nicht als solche behandelt werden. Ich würde auch den YAGNI Punkt kämpfen. Es ist nützlich , um die Lage sein , einen Test doppelt in Ihrem Unit - Test zu ersetzen. Alles Refactoring dieses nach der Tat zur Verfügung zu stellen ist ein Schmerz im Arsch . Planen Sie im Voraus und fallen nicht für die „YAGNI“ Entschuldigung
AlexFoxGill
11
@AlexG - eh ... in der Praxis ziemlich alle IoC Container als Fabriken arbeiten.
Telastyn
8
@AlexG Schwerpunkt der IOC konkrete Objekte zu konstruieren , um in andere Objekte vorbei basierend auf der Konfiguration / Konvention. Dies ist die gleiche Sache wie Fabrik verwendet wird . Und Sie brauchen keine Fabrik der Lage sein , ein Modell für den Test zu erstellen. Sie instanziieren einfach und übergeben den Schein direkt. Wie ich schon sagte in ersten Absatz. Fabriken sind nur nützlich , wenn Sie Erstellen einer Instanz zu Benutzer des Moduls weitergeben wollen, die die Fabrik nennt.
Euphoric
5
Es gibt einen Unterschied gemacht werden. Die Fabrik Muster werden von einem Verbraucher verwenden Einheiten während der Laufzeit eines Programms zu erstellen. Ein IoC - Container verwendet , um das Objektdiagramm des Programms während des Anlaufs zu erstellen. Sie können dies tun , von Hand ist der Behälter nur eine Bequemlichkeit. Der Verbraucher einer Fabrik sollte nicht bewusst sein , die IoC - Container.
AlexFoxGill
5
Re: „Und Sie brauchen nicht eine Fabrik in der Lage , ein Modell für den Test erstellen Sie nur instanziieren und die Schein-Pass direkt.“ - wieder andere Umstände. Sie verwenden ein Werk auf einen Fall bitten - der Verbraucher unter Kontrolle dieser Interaktion ist. Die Versorgung einer Instanz durch den Konstruktor oder eine Methode funktioniert nicht, es ist ein anderes Szenario.
AlexFoxGill
79

Die Fabrikmuster Mode stammt von einem fast dogmatischen Glauben unter den Programmierer in „C-Stil“ Sprachen (C / C ++, C #, Java), dass die Verwendung der „neuen“ Schlüsselwort ist schlecht, und sollte unter allen Umständen vermieden werden (oder zumin dest zentralisiert). Dies wiederum kommt aus einer ultra-strengen Auslegung der einheitlichen Prinzip Verantwortung (die „S“ von SOLID) und auch der Dependency Inversion Principle ( „D“). Einfach gesagt, sagt der SRP, dass im Idealfall ein Codeobjekt einen „Grund zu ändern“ haben sollte, und man nur; dass „Grund zu ändern“ ist der zentrale Zweck dieses Objekts, seine „Verantwortung“ in der Codebasis, und alles andere, was eine Änderung Code erfordert nicht Öffnung benötigen bis diese Klassendatei. Das DIP ist noch einfacher; ein Codeobjekt soll nie auf ein anderes konkretes Objekt abhängig sein,

Typischer Fall, durch „neue“ und einen öffentlichen Konstruktor verwenden, Sie koppeln den anrufenden Code auf eine bestimmte Bauweise einer bestimmten konkreten Klasse. Ihr Code muss jetzt wissen, dass eine Klasse MyFooObject existiert und hat einen Konstruktor, der einen String und einen int nimmt. Wenn das Konstruktor immer mehr Informationen benötigt, müssen alle Verwendungen des Konstrukteurs aktualisiert werden in diesen Informationen weitergeben, einschließlich der man jetzt Sie schreiben, und deshalb sind sie verpflichtet, etwas Gültigkeit zu haben, in übergeben, und so müssen sie entweder es oder geändert werden, um es (mehr Verantwortung an die raubend Objekte hinzufügen). Darüber hinaus müssen, wenn MyFooObject jemals in der Codebasis von BetterFooObject ersetzt wird, werden alle Verwendungen der alten Klasse ändern das neue Objekt anstelle der alten zu errichten.

Stattdessen sollten alle Benutzer von MyFooObject direkt von "IFooObject" abhängig sein, das das Verhalten der Implementierung von Klassen einschließlich MyFooObject definiert. Jetzt können Benutzer von IFooObjects nicht nur ein IFooObject erstellen (ohne zu wissen, dass eine bestimmte konkrete Klasse ein IFooObject ist, das sie nicht benötigen), sondern müssen stattdessen eine Instanz einer IFooObject-implementierenden Klasse oder Methode erhalten von außen durch ein anderes Objekt, das dafür verantwortlich ist, das richtige IFooObject für den Umstand zu erstellen, der in unserer Sprache normalerweise als Factory bezeichnet wird.

Hier trifft Theorie auf Realität. Ein Objekt kann niemals für alle Arten von Änderungen geschlossen werden. Beispiel: IFooObject ist jetzt ein zusätzliches Codeobjekt in der Codebasis, das geändert werden muss, wenn sich die von Verbrauchern oder Implementierungen von IFooObjects benötigte Schnittstelle ändert. Dies führt zu einer neuen Komplexität, die die Art und Weise beeinflusst, wie Objekte in dieser Abstraktion miteinander interagieren. Darüber hinaus müssen die Verbraucher noch tiefgreifender Änderungen vornehmen, wenn die Schnittstelle selbst durch eine neue ersetzt wird.

Ein guter Programmierer weiß, wie er YAGNI ("Du wirst es nicht brauchen") mit SOLID in Einklang bringt, indem er das Design analysiert und Orte findet, bei denen es sehr wahrscheinlich ist, dass sie sich auf eine bestimmte Weise ändern, und sie überarbeitet, um toleranter zu sein diese Art von Veränderung, denn in diesem Fall "wirst du es brauchen".

KeithS
quelle
21
Lieben Sie diese Antwort, besonders nachdem Sie alle anderen gelesen haben. Ich kann hinzufügen, dass fast alle (guten) neuen Programmierer über Prinzipien zu dogmatisch sind, weil sie wirklich gut sein wollen, aber noch nicht gelernt haben, wie wichtig es ist, Dinge einfach zu halten und nicht zu viel zu tun.
jean
1
Ein weiteres Problem mit öffentlichen Konstruktoren besteht darin, dass es für eine Klasse keine gute Möglichkeit gibt Foo, einen öffentlichen Konstruktor anzugeben, der zum Erstellen von FooInstanzen oder zum Erstellen anderer Typen innerhalb desselben Pakets / derselben Assembly verwendet werden kann , der jedoch nicht zum Erstellen von verwendet werden kann anderswo abgeleitete Typen. Ich kenne keinen besonders zwingenden Grund, warum eine Sprache / ein Framework keine separaten Konstruktoren zur Verwendung in newAusdrücken definieren kann, im Gegensatz zum Aufruf von Subtypkonstruktoren, aber ich kenne keine Sprachen, die diese Unterscheidung treffen.
Supercat
1
Ein geschützter und / oder interner Konstruktor wäre ein solches Signal; Dieser Konstruktor kann nur Code in einer Unterklasse oder in derselben Assembly verarbeiten. C # hat keine Schlüsselwortkombination für "geschützt und intern", was bedeutet, dass nur Subtypen innerhalb der Assembly sie verwenden können. MSIL verfügt jedoch über eine Bereichs-ID für diese Art der Sichtbarkeit, sodass es denkbar ist, dass die C # -Spezifikation erweitert werden kann, um eine Möglichkeit zur Verwendung zu bieten . Dies hat jedoch nicht viel mit der Verwendung von Fabriken zu tun (es sei denn, Sie verwenden die Einschränkung der Sichtbarkeit, um die Verwendung einer Fabrik zu erzwingen).
KeithS
2
Perfekte Antwort. Genau auf den Punkt mit dem Teil "Theorie trifft Realität". Denken Sie nur daran, wie viele Tausende von Entwicklern und Menschen Zeit damit verbracht haben, den Frachtkult zu loben, zu rechtfertigen, umzusetzen und sie dann zu nutzen, um in die von Ihnen beschriebene Situation zu geraten. Nach YAGNI habe ich nie die Notwendigkeit erkannt, eine Fabrik einzurichten
Breno Salgado,
Ich programmiere auf einer Codebasis, bei der die Verwendung öffentlicher Konstruktoren effektiv verboten ist, da die OOP-Implementierung keine Überlastung des Konstruktors zulässt. Dies ist bereits ein kleines Problem in Sprachen, die dies zulassen. Wie schaffen Temperatur. Sowohl Fahrenheit als auch Celsius sind Schwimmer. Du kannst die aber boxen, dann Problem gelöst.
jgmjgm
33

Konstruktoren sind in Ordnung, wenn sie kurzen, einfachen Code enthalten.

Wenn die Initialisierung mehr als die Zuweisung einiger Variablen zu den Feldern ist, ist eine Factory sinnvoll. Hier sind einige der Vorteile:

  • Langer, komplizierter Code ist in einer dedizierten Klasse (einer Factory) sinnvoller. Wenn derselbe Code in einem Konstruktor abgelegt wird, der eine Reihe statischer Methoden aufruft, wird die Hauptklasse verschmutzt.

  • In einigen Sprachen und in einigen Fällen ist das Auslösen von Ausnahmen in Konstruktoren eine wirklich schlechte Idee , da dies zu Fehlern führen kann.

  • Wenn Sie einen Konstruktor aufrufen, müssen Sie als Aufrufer den genauen Typ der zu erstellenden Instanz kennen. Dies ist nicht immer der Fall (als Feedermuss ich nur das konstruieren, Animalum es zu füttern; es ist mir egal, ob es ein Dogoder ein ist Cat).

Arseni Mourzenko
quelle
2
Die Auswahlmöglichkeiten sind nicht nur "Factory" oder "Konstruktor". A verwendet Feedermöglicherweise keine der beiden Methoden und ruft stattdessen Kenneldie getHungryAnimalMethode des Objekts auf .
DougM
4
+1 Ich finde, dass es keine schlechte Idee ist, sich an eine Regel zu halten, die absolut keine Logik enthält. Ein Konstruktor sollte nur zum Festlegen des Anfangszustands des Objekts verwendet werden, indem seine Argumentwerte Instanzvariablen zugewiesen werden. Wenn etwas Komplizierteres erforderlich ist, erstellen Sie mindestens eine Factory (Klassen) -Methode, um die Instanz zu erstellen.
KaptajnKold
Dies ist die einzige befriedigende Antwort, die ich hier gesehen habe und die mich davon befreit hat, meine eigene schreiben zu müssen. Die anderen Antworten befassen sich nur mit abstrakten Begriffen.
TheCatWhisperer
Aber dieses Argument kann auch für gültig gehalten werden Builder Pattern. Ist es nicht
Soufrk
Konstrukteure Regel
Breno Salgado
10

Wenn Sie mit Schnittstellen arbeiten, können Sie unabhängig von der tatsächlichen Implementierung bleiben. Eine Factory kann konfiguriert werden (über Eigenschaften, Parameter oder eine andere Methode), um eine von mehreren verschiedenen Implementierungen zu instanziieren.

Ein einfaches Beispiel: Sie möchten mit einem Gerät kommunizieren, wissen aber nicht, ob dies über Ethernet, COM oder USB erfolgen soll. Sie definieren eine Schnittstelle und 3 Implementierungen. Zur Laufzeit können Sie dann die gewünschte Methode auswählen und die Factory gibt Ihnen die entsprechende Implementierung.

Benutze es oft ...

paul
quelle
5
Ich würde hinzufügen, dass es großartig ist, wenn es mehrere Implementierungen eines Interfaces gibt und der aufrufende Code nicht weiß oder nicht wissen sollte, welches er auswählen soll. Aber wenn eine Factory - Methode um einen einzigen Konstruktor wie in der Frage einfach eine statische Wrapper ist, dass das anti-Muster. Es muss mehrere Implementierungen geben, aus denen ausgewählt werden muss, da sonst die Fabrik im Weg ist und unnötige Komplexität hinzugefügt wird.
Jetzt haben Sie die zusätzliche Flexibilität und theoretisch kann Ihre Anwendung Ethernet, COM, USB und seriell verwenden. Theoretisch ist sie bereit für Fireloop oder was auch immer. In Wirklichkeit kommuniziert Ihre App nur über Ethernet .... je.
Pieter B
7

Dies ist ein Symptom für eine Einschränkung in Java / C # -Modulsystemen.

Grundsätzlich gibt es keinen Grund, eine Implementierung einer Klasse mit denselben Konstruktor- und Methodensignaturen gegen eine andere auszutauschen. Es gibt Sprachen, die dies erlauben. Java und C # bestehen jedoch darauf, dass jede Klasse einen eindeutigen Bezeichner (den vollständig qualifizierten Namen) hat und der Clientcode eine fest codierte Abhängigkeit davon aufweist.

Sie können dies irgendwie umgehen, indem Sie mit den Dateisystem- und Compileroptionen experimentieren, sodass eine com.example.FooZuordnung zu einer anderen Datei möglich ist. Dies ist jedoch überraschend und nicht intuitiv. Selbst wenn Sie dies tun, ist Ihr Code immer noch an nur eine Implementierung der Klasse gebunden. Dh, wenn Sie eine Klasse schreiben, die von einer Klasse Fooabhängt MySet, können Sie MySetzur Kompilierungszeit eine Implementierung von auswählen , aber Sie können Foos immer noch nicht mit zwei verschiedenen Implementierungen von instanziieren MySet.

Diese unglückliche Designentscheidung zwingt die Leute dazu, interfaces unnötig zu verwenden, um ihren Code gegen die Möglichkeit, dass sie später eine andere Implementierung von etwas benötigen, zukunftssicher zu machen oder Unit-Tests zu ermöglichen. Das ist nicht immer machbar; Wenn Sie Methoden haben, die die privaten Felder von zwei Instanzen der Klasse untersuchen, können Sie diese nicht in einer Schnittstelle implementieren. Das ist der Grund, warum Sie zum Beispiel nicht unionin Javas SetBenutzeroberfläche sehen. Abgesehen von numerischen Typen und Auflistungen sind binäre Methoden jedoch nicht üblich, sodass Sie in der Regel davonkommen können.

Wenn Sie aufrufen, haben new Foo(...)Sie natürlich immer noch eine Abhängigkeit von der Klasse . Sie benötigen also eine Factory, wenn eine Klasse eine Schnittstelle direkt instanziieren soll. In der Regel ist es jedoch besser, die Instanz im Konstruktor zu akzeptieren und eine andere Person entscheiden zu lassen, welche Implementierung verwendet werden soll.

Es liegt an Ihnen, zu entscheiden, ob es sich lohnt, Ihre Codebasis mit Schnittstellen und Fabriken aufzublähen. Wenn die betreffende Klasse in Ihrer Codebasis enthalten ist, ist es zum einen trivial, den Code so umzugestalten, dass er in Zukunft eine andere Klasse oder ein anderes Interface verwendet. Sie können YAGNI später aufrufen und umgestalten, wenn die Situation eintritt. Wenn die Klasse jedoch Teil der öffentlichen API einer von Ihnen veröffentlichten Bibliothek ist, können Sie den Clientcode nicht korrigieren. Wenn Sie eine nicht verwenden interfaceund später mehrere Implementierungen benötigen, stecken Sie zwischen einem Stein und einem harten Ort fest.

Doval
quelle
2
Ich wünschte, Java und Derivate wie .NET hätten eine spezielle Syntax für eine Klasse, die Instanzen von sich selbst erstellt, und newwären ansonsten einfach syntaktischer Zucker für den Aufruf einer speziell benannten statischen Methode (die automatisch generiert würde, wenn ein Typ einen "öffentlichen Konstruktor" hätte "aber die Methode nicht explizit einbinden). IMHO, wenn Code nur eine langweilige Standard-Implementierung haben möchte List, sollte es der Schnittstelle möglich sein, eine solche zu erstellen, ohne dass der Client eine bestimmte Implementierung kennen muss (z ArrayList. B. ).
Supercat
2

Meiner Meinung nach verwenden sie nur die einfache Fabrik, die kein geeignetes Entwurfsmuster darstellt und nicht mit der abstrakten Fabrik oder der Fabrikmethode verwechselt werden sollte.

Und da sie eine "riesige Fabric-Klasse mit Tausenden von statischen CreateXXX-Methoden" erstellt haben, klingt dies nach einem Anti-Pattern (vielleicht nach einer God-Klasse?).

Ich denke, dass die Simple Factory und die statischen Erstellermethoden (für die keine externe Klasse erforderlich ist) in einigen Fällen nützlich sein können. Zum Beispiel, wenn für die Konstruktion eines Objekts verschiedene Schritte erforderlich sind, z. B. das Instanziieren anderer Objekte (z. B. Begünstigen der Komposition).

Ich würde das nicht einmal als Factory bezeichnen, sondern nur eine Reihe von Methoden, die in einer zufälligen Klasse mit dem Suffix "Factory" gekapselt sind.

FranMowinckel
quelle
1
Simple Factory hat seinen Platz. Stellen Sie sich vor, eine Entität übernimmt zwei Konstruktorparameter int xund IFooService fooService. Sie möchten nicht fooServiceüberall herumlaufen, also erstellen Sie eine Fabrik mit einer Methode Create(int x)und fügen den Service in die Fabrik ein.
AlexFoxGill
4
@AlexG Und dann musst du IFactoryüberall rumlaufen anstatt IFooService.
Euphoric
3
Ich stimme mit Euphoric überein. Nach meiner Erfahrung sind Objekte, die von oben in ein Diagramm eingefügt werden, in der Regel von einem Typ, den alle untergeordneten Objekte benötigen. Daher ist es keine große Sache, IFooServices weiterzugeben. Das Ersetzen einer Abstraktion durch eine andere bewirkt nichts anderes, als die Codebasis weiter zu verschleiern.
KeithS
Dies ist für die Frage "Warum sollte ich eine Factory-Klasse anstelle einer direkten Objektkonstruktion verwenden? Wann sollte ich öffentliche Konstruktoren verwenden und wann sollten Fabriken verwendet werden?" völlig irrelevant. Sehen Sie, wie man antwortet
Mücke
1
Das ist nur der Titel der Frage, ich denke, Sie haben den Rest verpasst. Siehe letzten angegebenen Linkpunkt;).
FranMowinckel
1

Wenn die Bibliothek als Benutzer einer Bibliothek über Factory-Methoden verfügt, sollten Sie diese verwenden. Sie gehen davon aus, dass die Factory-Methode dem Autor der Bibliothek die Flexibilität gibt, bestimmte Änderungen vorzunehmen, ohne dass sich dies auf Ihren Code auswirkt. Sie können beispielsweise eine Instanz einer Unterklasse in einer Factory-Methode zurückgeben, die mit einem einfachen Konstruktor nicht funktioniert.

Als Ersteller einer Bibliothek würden Sie Factory-Methoden verwenden, wenn Sie diese Flexibilität selbst nutzen möchten.

In dem Fall, den Sie beschreiben, scheinen Sie den Eindruck zu haben, dass das Ersetzen der Konstruktoren durch Factory-Methoden einfach sinnlos war. Es war sicherlich ein Schmerz für alle Beteiligten; Eine Bibliothek sollte nichts ohne guten Grund aus ihrer API entfernen. Wenn ich also Factory-Methoden hinzugefügt hätte, hätte ich vorhandene Konstruktoren verfügbar gelassen, möglicherweise veraltet, bis eine Factory-Methode diesen Konstruktor nicht mehr nur aufruft und Code mit dem einfachen Konstruktor weniger gut funktioniert, als er sollte. Ihr Eindruck könnte sehr gut richtig sein.

gnasher729
quelle
Beachten Sie auch; Wenn der Entwickler einer abgeleiteten Klasse zusätzliche Funktionen (zusätzliche Eigenschaften, Initialisierung) zur Verfügung stellen muss, kann er dies tun. (Vorausgesetzt, die Factory-Methoden sind überschreibbar). Der API-Autor kann auch eine Problemumgehung für spezielle Kundenanforderungen bereitstellen.
Eric Schneider
-4

Dies scheint im Zeitalter von Scala und funktionaler Programmierung überholt zu sein. Ein solides Funktionsfundament ersetzt eine Unmenge von Klassen.

Auch zu beachten, dass Java Double {{nicht mehr funktioniert, wenn eine Fabrik verwendet wird, dh

someFunction(new someObject() {{
    setSomeParam(...);
    etc..
}})

Damit können Sie eine anonyme Klasse erstellen und anpassen.

Im Zeit-Raum-Dilema ist der Zeitfaktor dank schneller CPUs jetzt so stark geschrumpft, dass eine funktionale Programmierung, die das Schrumpfen des Raums bzw. der Codegröße ermöglicht, jetzt praktisch ist.

Marc
quelle
2
Dies sieht eher aus wie ein tangentialer Kommentar (siehe Wie man antwortet), und es scheint nichts Wesentliches über die Punkte zu bieten, die in den vorherigen 9 Antworten gemacht und erklärt wurden
Mücke