Welche Art der Initialisierung wird beim Modellieren von Klassen bevorzugt:
- Konstruktoren oder
- Fabrikmethoden
Und was wären die Überlegungen für die Verwendung von beiden?
In bestimmten Situationen bevorzuge ich eine Factory-Methode, die null zurückgibt, wenn das Objekt nicht erstellt werden kann. Dies macht den Code ordentlich. Ich kann einfach überprüfen, ob der zurückgegebene Wert nicht null ist, bevor ich eine alternative Aktion ausführe, im Gegensatz zum Auslösen einer Ausnahme vom Konstruktor. (Ich persönlich mag keine Ausnahmen)
Angenommen, ich habe einen Konstruktor für eine Klasse, der einen ID-Wert erwartet. Der Konstruktor verwendet diesen Wert, um die Klasse aus der Datenbank zu füllen. Wenn kein Datensatz mit der angegebenen ID vorhanden ist, löst der Konstruktor eine RecordNotFoundException aus. In diesem Fall muss ich die Konstruktion all dieser Klassen in einen try..catch-Block einschließen.
Im Gegensatz dazu kann ich für diese Klassen eine statische Factory-Methode verwenden, die null zurückgibt, wenn der Datensatz nicht gefunden wird.
Welcher Ansatz ist in diesem Fall besser, Konstruktor oder Fabrikmethode?
Fragen Sie sich, was sie sind und warum wir sie haben. Beide dienen dazu, eine Instanz eines Objekts zu erstellen.
Bisher kein Unterschied. Stellen Sie sich nun vor, wir haben verschiedene Schultypen und möchten von ElementarySchool zu HighSchool wechseln (das von einer ElementarySchool abgeleitet ist oder dieselbe Schnittstellen-ISchool wie die ElementarySchool implementiert). Die Codeänderung wäre:
Im Falle einer Schnittstelle hätten wir:
Wenn Sie diesen Code nun an mehreren Stellen haben, können Sie sehen, dass die Verwendung der Factory-Methode ziemlich billig sein kann, da Sie nach dem Ändern der Factory-Methode fertig sind (wenn wir das zweite Beispiel mit Schnittstellen verwenden).
Und das ist der Hauptunterschied und Vorteil. Wenn Sie sich mit komplexen Klassenhierarchien befassen und dynamisch eine Instanz einer Klasse aus einer solchen Hierarchie erstellen möchten, erhalten Sie den folgenden Code. Factory-Methoden verwenden dann möglicherweise einen Parameter, der der Methode mitteilt, welche konkrete Instanz instanziiert werden soll. Angenommen, Sie haben eine MyStudent-Klasse und müssen das entsprechende ISchool-Objekt instanziieren, damit Ihr Schüler Mitglied dieser Schule ist.
Jetzt haben Sie eine Stelle in Ihrer App, die Geschäftslogik enthält, die bestimmt, welches ISchool-Objekt für verschiedene IStudent-Objekte instanziiert werden soll.
Also - für einfache Klassen (Wertobjekte usw.) ist der Konstruktor in Ordnung (Sie möchten Ihre Anwendung nicht überentwickeln), aber für komplexe Klassenhierarchien ist die Factory-Methode eine bevorzugte Methode.
Auf diese Weise folgen Sie dem ersten Entwurfsprinzip der vierköpfigen Gruppe "Programm zu einer Schnittstelle, nicht zu einer Implementierung".
quelle
IFood sandwich = new Sandwich(Cheese chz, Meat meat);
UndIFood soup = new Soup(Broth broth, Vegetable veg);
wie können Fabrik und / oder Baumeister hier helfen?Sie müssen lesen (wenn Sie Zugriff darauf haben). Effektives Java 2 Punkt 1: Betrachten Sie statische Factory-Methoden anstelle von Konstruktoren .
Vorteile der statischen Fabrikmethoden:
Nachteile der statischen Fabrikmethoden:
quelle
( factory methods are better than Constructors. ( Item-1 ) ) Effective java
Standardmäßig sollten Konstruktoren bevorzugt werden, da sie einfacher zu verstehen und zu schreiben sind. Wenn Sie jedoch speziell die Konstruktionsmerkmale eines Objekts von seiner semantischen Bedeutung im Sinne des Client-Codes entkoppeln müssen, sollten Sie Fabriken verwenden.
Der Unterschied zwischen Konstruktoren und Fabriken ist beispielsweise analog zu einer Variablen und einem Zeiger auf eine Variable. Es gibt eine andere Ebene der Indirektion, was ein Nachteil ist. Es gibt aber auch ein anderes Maß an Flexibilität, was von Vorteil ist. Wenn Sie also eine Wahl treffen, sollten Sie diese Kosten-Nutzen-Analyse durchführen.
quelle
Verwenden Sie eine Factory nur, wenn Sie zusätzliche Kontrolle bei der Objekterstellung benötigen, wie dies mit Konstruktoren nicht möglich ist.
Fabriken haben beispielsweise die Möglichkeit, zwischenzuspeichern.
Eine andere Möglichkeit, Fabriken zu verwenden, besteht in einem Szenario, in dem Sie den Typ, den Sie erstellen möchten, nicht kennen. Oft sehen Sie diese Art der Verwendung in Plugin-Factory-Szenarien, in denen jedes Plugin von einer Basisklasse abgeleitet sein oder eine Art Schnittstelle implementieren muss. Die Factory erstellt Instanzen von Klassen, die von der Basisklasse abgeleitet sind oder die Schnittstelle implementieren.
quelle
Ein Zitat aus "Effective Java", 2. Aufl., Punkt 1: Betrachten Sie statische Factory-Methoden anstelle von Konstruktoren. 5:
"Beachten Sie, dass eine statische Factory-Methode nicht mit dem Factory-Methodenmuster aus Design Patterns [Gamma95, S. 107] identisch ist. Die in diesem Artikel beschriebene statische Factory-Methode hat keine direkte Entsprechung in Design Patterns."
quelle
Neben "effektivem Java" (wie in einer anderen Antwort erwähnt) schlägt ein anderes klassisches Buch auch vor:
Z.B. schreibe nicht
sondern schreiben
Das Buch geht so weit, den
Complex(float)
Konstruktor privat zu machen, um den Benutzer zu zwingen, die statische Factory-Methode aufzurufen.quelle
from…
,to…
,parse…
,with…
, und so weiter. Beachten Sie, dass die java.time- Klassen unveränderlich aufgebaut sind. Einige dieser Namenskonventionen können jedoch auch für veränderbare Klassen hilfreich sein.Ein konkretes Beispiel aus einer CAD / CAM-Anwendung.
Ein Schneidpfad würde unter Verwendung eines Konstruktors erstellt. Es ist eine Reihe von Linien und Bögen, die einen zu schneidenden Pfad definieren. Die Reihen von Linien und Bögen können unterschiedlich sein und unterschiedliche Koordinaten haben. Sie können jedoch einfach durch Übergeben einer Liste an einen Konstruktor verarbeitet werden.
Eine Form würde unter Verwendung einer Fabrik hergestellt werden. Denn während es eine Formklasse gibt, wird jede Form je nach Art der Form unterschiedlich eingerichtet. Wir wissen nicht, welche Form wir initialisieren werden, bis der Benutzer eine Auswahl trifft.
quelle
Dieser Prozess sollte definitiv außerhalb eines Konstruktors liegen.
Der Konstruktor sollte nicht auf die Datenbank zugreifen.
Die Aufgabe und der Grund für einen Konstruktor besteht darin, Datenelemente zu initialisieren und eine Klasseninvariante unter Verwendung von Werten zu etablieren, die an den Konstruktor übergeben werden.
Für alles andere ist es besser, die statische Factory-Methode oder in komplexeren Fällen eine separate Factory oder einen Builder zu verwenden Klasse zu verwenden.
Einige Konstruktorrichtlinien von Microsoft :
Und
quelle
Manchmal müssen Sie beim Erstellen eines Objekts einige Werte / Bedingungen überprüfen / berechnen. Und wenn es eine Ausnahme auslösen kann - Konstruktro ist ein sehr schlechter Weg. Sie müssen also so etwas tun:
Wo alle zusätzlichen Berechnungen in init () sind. Aber nur Sie als Entwickler wissen wirklich über dieses init () Bescheid. Und natürlich vergisst man es nach Monaten einfach. Aber wenn Sie eine Fabrik haben - tun Sie einfach alles, was Sie brauchen, in einer Methode, indem Sie diese init () vor dem direkten Aufruf verstecken - also keine Probleme. Mit diesem Ansatz ist es kein Problem, auf die Erstellung zu fallen und Speicher zu verlieren.
Jemand hat dir von Caching erzählt. Das ist gut. Sie müssen sich aber auch an das Flyweight-Muster erinnern, das sich gut für die Factory-Methode eignet.
quelle