Ich bin neu in OOP und lerne Designmuster, also schrieb ich einen einfachen Code, um eine Factory-Methode auszuprobieren, und alles scheint gut, außer wenn ich einen anderen Subtyp hinzufügen möchte. Hier ist der Code soweit:
%Vor% Wenn die Anforderung später geändert wird, um eine Unterklasse Pensioner
einzufügen, wenn das Alter & gt; 70, ich müsste entweder:
Fügen Sie der if (age > 70) return new Pensioner();
-Methode in der Klasse create()
die Zeile PersonFactory
hinzu, die das Open-Closed-Prinzip sicher unterbricht?
Oder, wie im Buch The Gang Of Four Design Patterns vorgeschlagen, überschreiben Sie die parametrisierte Factory-Methode, um die Produkte, die ein Creator erzeugt, selektiv zu erweitern. In diesem Fall würde das wohl bedeuten, eine neue Klasse zu schreiben:
Das bedeutet nun, dass entweder alle Clients, die PersonFactory
aufrufen, geändert werden müssen, um stattdessen PersonFactoryWithPensioner
zu verwenden, oder ich muss akzeptieren, dass neue Clients PersonFactoryWithPensioner
aufrufen können, während die alten Clients z. ClientA
würde immer noch nur ein Adult
-Objekt erhalten, wenn das Alter & gt; 70. Es wird noch schlimmer, wenn später eine andere Unterklasse, z. Infant
wird hinzugefügt. Um sicherzustellen, dass die neuen Kunden das Objekt Infant
, Child
, Adult
oder Pensioner
erhalten, muss eine neue Klasse PersonFactoryWithInfant
PersonFactoryWithPensioner
erweitern. Das kann nicht richtig sein, scheint eher missverstanden zu haben, was GoF vorschlägt.
Meine Frage ist: Gibt es eine Möglichkeit, einen neuen Untertyp hinzuzufügen, der an alte Clients zurückgegeben werden kann, ohne sie zu ändern, und ohne den OCP zu ändern, indem der PersonFactory
-Code so geändert wird, dass er den neuen Untertyp enthält?
Entschuldigung, wenn ich das nicht richtig gepostet habe, stelle ich hier zum ersten Mal eine Frage. Ich habe vorangegangene Antworten für ähnliche Probleme durchgesehen, aber sie scheinen sich damit nicht zu befassen.
Ich denke, OCP hört nicht damit auf, irgendeine Methode oder Klasse zu modifizieren.
Aber es schlägt vor, dass Sie, wenn Sie Änderungen vornehmen müssen, dies tun sollten, damit Sie diesen Code nicht noch einmal ändern müssen.
Da Sie möglicherweise später PersonFactory
ändern müssen, können Sie eine weitere Klasse Factory
erstellen, um Objekte vom Typ PersonFactory
zu erstellen. Obwohl dies wie überentwickelte Lösung aussieht.
Eine andere mögliche Lösung wäre, dass PersonFactory
diese Regeln aus einer dynamischen Quelle lädt, zum Beispiel diese Regeln in einer Datei mit dem JSON-Format speichert.
Und dann Objekte dynamisch mit Reflektion erstellen.
In etwa so:
%Vor%Die json-Regeln würden etwa so aussehen:
%Vor%Auf diese Weise brechen Sie das OCP-Prinzip nicht.
Das Prinzip "offen-geschlossen" ist gut zu beachten. Es funktioniert jedoch nicht gut mit Fabriken. Eine Option dieser Art von Arbeiten ist die folgende, die die Fabrik in eine Registrierung verwandelt:
%Vor% Ähnlich wie @alayor die Antwort, der einzige Weg, um zu vermeiden, die Logik der Fabrik zu ändern, oder die Fabrik komplett austauschen zu müssen und alle dazu zu bringen, die neue Version zu benutzen ... ist für die Fabrik, um ihre Logik von woanders zu bekommen. @alayor ruft es aus einer Konfigurationsdatei ab; Ich schlage vor, es als Teil seiner Initialisierung der Fabrik hinzuzufügen (könnte auch im Fabrikkonstruktor gemacht werden; ändern Sie es zum Beispiel in public PersonFactory(PersonCreator ... rules)
).
Vollständiger Code:
%Vor%Regeln sind manchmal dazu gedacht, gebrochen zu werden, also sage ich BREAK das Geschlossene Prinzip, um es sauber und einfach zu halten. Der Mehraufwand beim Erstellen mehrerer Factory-Klassen für jede Art von Person bricht meiner Meinung nach den gesamten Zweck der Factory-Methode. Wenn Sie das Closed-Prinzip umgehen, können Sie eine einzelne -Klasse zum Erstellen eines beliebigen Personentyps erstellen.
%Vor%Alle Antworten hier, die darauf hindeuten, dass eine Art von dynamischen Regeln das Prinzip des Aufbrechens / Schließens aufbrechen. Bei diesem Prinzip geht es nicht darum, "einen Code, der bereits geschrieben wurde, nicht zu ändern", sondern "das Ergebnis eines bereits verwendeten Codes nicht zu ändern". Das heißt, wenn ein Client erwartet, dass er nur zwei Ergebnisse erhalten kann - Adult oder Child, die dritte Möglichkeit entweder durch Festcodierung in Funktion oder durch dynamische Regelsätze bietet, bricht das Open-Close-Prinzip.
Aber zurück zu Ihrer Frage - ich werde sagen, es kommt darauf an. Prinzipien und Muster sind nett, lustig und alles andere als in der realen täglichen Arbeit muss man immer auf das große Bild schauen und entscheiden, ob man bestimmte Regeln anwendet oder nicht. Behandle sie als Andeutungen, nicht als etwas, das in Stein geschrieben ist.
Wenn Ihr Code etwas geschlossen ist, haben Sie die Kontrolle über jeden Aufruf von PersonFactory, dann sind Änderungen im Lebenszyklus Ihrer Software eine normale Sache. Ich erinnere mich nicht an ein echtes Lebensprojekt, an dem ich teilgenommen habe und das keinen Code verändert hat, der zuvor erstellt wurde. Tatsächlich machen wir es täglich:)
Eine andere Sache ist, wenn Ihr Code von einer unbekannten Anzahl von Clients von Drittanbietern verwendet wird (z. B. öffentliche API). Dann sollten Sie vorsichtig sein, um etwas nicht zu brechen, aber auch neue Logik in bestehenden Methoden zu präsentieren (wie hier, wenn Sie ein neues Konzept von Person hinzufügen) ist vollkommen akzeptabel. Wenn diese Änderungen brechen würden, dann erwägen Sie, eine neue / aktualisierte Version des geänderten Codes neben der alten hinzuzufügen (und möglicherweise einen Plan der alten Version irgendwann in der Zukunft zu verwerfen, da Sie wirklich nicht 10000 Versionen Ihres Codes pflegen wollen; )
Denken Sie auch an andere OOP-Teile, die Ihnen helfen sollten, einige Probleme zu vermeiden. In Ihrem Beispiel implementiert Adult, Child und Rentner Person Interface, was großartig ist. Jeder Code, der nur Adult- und Child-Implementierungen kennt, sollte kein Problem mit dem Rentner-Wert haben, da alle nur Personalimplementierungen sind und dieser Code Rentner auch als Person behandeln sollte, ohne zu wissen, dass Sie einen neuen Typ eingeführt haben.
Tags und Links java solid-principles factory-method open-closed-principle