Zum Beispiel:
var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)
Da die Duck-Klasse alle Verhaltensweisen enthält (abstrakt), scheint das Erstellen einer neuen Klasse MallardDuck
(die erweitert wird Duck
) nicht erforderlich zu sein.
Referenz: Head First Design Pattern, Kapitel 1.
design-patterns
dependency-injection
strategy-pattern
Deepak Mishra
quelle
quelle
Duckbehavior.quackBehavior
und andere Felder enthält Ihr Code?Antworten:
Sicher, aber wir nennen das Zusammensetzung und Delegation . Das Strategiemuster und die Abhängigkeitsinjektion mögen strukturell ähnlich erscheinen, aber ihre Absichten sind unterschiedlich.
Das Strategiemuster ermöglicht eine Laufzeitänderung des Verhaltens unter derselben Schnittstelle. Ich konnte einer Stockente sagen, sie solle fliegen und sie mit Flügeln fliegen sehen. Tauschen Sie es dann gegen eine Jet-Pilot-Ente aus und beobachten Sie, wie es mit Delta Airlines fliegt. Dies zu tun, während das Programm ausgeführt wird, ist eine Strategie-Muster-Sache.
Die Abhängigkeitsinjektion ist eine Technik zur Vermeidung von Abhängigkeiten bei der harten Codierung, sodass sie unabhängig voneinander geändert werden können, ohne dass Clients bei Änderungen geändert werden müssen. Kunden drücken einfach ihre Bedürfnisse aus, ohne zu wissen, wie sie erfüllt werden. Daher wird an anderer Stelle (in der Regel hauptsächlich) entschieden, wie sie erfüllt werden. Sie brauchen keine zwei Enten, um diese Technik anzuwenden. Nur etwas, das eine Ente benutzt, ohne zu wissen oder sich darum zu kümmern, welche Ente. Etwas, das die Ente nicht baut oder danach sucht, aber vollkommen glücklich ist, die Ente zu verwenden, die Sie ihr geben.
Wenn ich eine konkrete Entenklasse habe, kann ich sie das Flugverhalten implementieren lassen. Ich könnte sogar das Verhalten von Fly-with-Wings auf Fly-with-Delta basierend auf einer Zustandsvariablen ändern lassen. Diese Variable kann ein Boolescher Wert, ein Int oder
FlyBehavior
einefly
Methode sein , die eine Methode hat, die jeden Flugstil ausführt, ohne dass ich sie mit einem if testen muss. Jetzt kann ich den Flugstil ändern, ohne den Ententyp zu ändern. Jetzt können Stockenten Piloten werden. Dies ist Zusammensetzung und Delegation . Die Ente besteht aus einem FlyBehavior und kann Fluganfragen an sie delegieren. Auf diese Weise können Sie alle Verhaltensweisen Ihrer Enten auf einmal ersetzen oder für jedes Verhalten oder eine beliebige Kombination dazwischen etwas halten.Dies gibt Ihnen alle die gleichen Kräfte, die die Vererbung außer einer hat. Mit der Vererbung können Sie ausdrücken, welche Duck-Methoden Sie in den Duck-Subtypen überschreiben. Für die Komposition und Delegierung muss die Ente von Anfang an explizit an Subtypen delegieren. Dies ist weitaus flexibler, erfordert jedoch mehr Tastatureingaben und Duck muss wissen, dass dies geschieht.
Viele Menschen glauben jedoch, dass die Vererbung von Anfang an explizit ausgelegt werden muss. Und wenn dies nicht der Fall ist, sollten Sie Ihre Klassen als versiegelt / endgültig markieren, um die Vererbung zu verbieten. Wenn Sie diese Ansicht vertreten, hat die Vererbung keinen Vorteil gegenüber der Zusammensetzung und Delegierung. Denn dann muss man so oder so entweder von Anfang an auf Erweiterbarkeit ausgelegt sein oder bereit sein, Dinge später abzureißen.
Abreißen ist eigentlich eine beliebte Option. Seien Sie sich nur bewusst, dass es Fälle gibt, in denen es ein Problem gibt. Wenn Sie Bibliotheken oder Codemodule unabhängig voneinander bereitgestellt haben, die Sie mit der nächsten Version nicht aktualisieren möchten, kann es sein, dass Sie nicht mehr mit Versionen von Klassen zu tun haben, die nichts über Ihre aktuellen Aktivitäten wissen.
Die Bereitschaft, Dinge später abzureißen, kann Sie von übermäßigem Entwerfen befreien. Es ist jedoch sehr mächtig, etwas zu entwerfen, das eine Ente verwendet, ohne wissen zu müssen, was die Ente tatsächlich tun wird, wenn sie verwendet wird. Das Nichtwissen ist mächtiges Zeug. So können Sie für eine Weile aufhören, an Enten zu denken, und über den Rest Ihres Codes nachdenken.
"Können wir" und "sollten wir" sind unterschiedliche Fragen. Die Komposition gegenüber der Vererbung zu bevorzugen bedeutet nicht, niemals die Vererbung zu verwenden. Es gibt immer noch Fälle, in denen Vererbung am sinnvollsten ist. Ich zeige Ihnen mein Lieblingsbeispiel :
Mit der Vererbung können Sie Ausnahmen mit spezifischeren, beschreibenden Namen in nur einer Zeile erstellen.
Versuchen Sie das mit Komposition und Sie werden ein Chaos bekommen. Es besteht auch kein Risiko für das Vererbungs- Jojo-Problem, da hier keine Daten oder Methoden zur Wiederverwendung und Förderung der Vererbungsverkettung vorhanden sind. Das alles fügt einen guten Namen hinzu. Unterschätze niemals den Wert eines guten Namens.
quelle
LoginException
ist kein guter Name. Es ist ein klassisches "Schlumpf-Benennen" und wenn ich es wegnehmeException
, ist alles, was ich habeLogin
, es sagt mir nichts darüber, was schief gelaufen ist.Exception
jede Ausnahmeklasse zu beenden, aber bitte verwechseln Sie "festgefahren" nicht mit "gut".StackOverflow
,OutOfMemory
,NullReferenceAccess
,LoginFailure
usw. Grundsätzlich nehmen „Exception“ aus dem Namen. Und wenn nötig, beheben Sie sie so, dass beschrieben wird, was schief gelaufen ist.Sie können fast jede Methodik durch eine andere Methodik ersetzen und trotzdem funktionierende Software erstellen. Einige passen jedoch besser zu einem bestimmten Problem als andere.
Es kommt auf viele Dinge an, die man vorziehen kann. Stand der Technik in der Anwendung, Erfahrung im Team, erwartete zukünftige Entwicklungen, persönliche Vorlieben und wie schwierig es für einen Neuling wahrscheinlich sein wird, sich damit auseinanderzusetzen, um nur einige zu nennen.
Wenn Sie erfahrener werden und häufiger mit den Kreationen anderer Menschen zu kämpfen haben, werden Sie wahrscheinlich mehr Wert auf die letzte Betrachtung legen.
Vererbung ist immer noch ein gültiges und starkes Modellierungswerkzeug, das nicht unbedingt immer das flexibelste ist, aber neuen Leuten eine starke Anleitung bietet, die möglicherweise für die eindeutige Zuordnung zur Problemdomäne dankbar sind.
quelle
Vererbung ist nicht so wichtig wie früher gedacht. Es ist immer noch wichtig , und es zu entfernen wäre ein schwerer Fehler.
Ein extremes Beispiel ist Objective-C, bei dem alles eine Unterklasse von NSObject ist (der Compiler erlaubt Ihnen nicht, eine Klasse zu deklarieren, die keine Basisklasse hat, daher muss alles , was nicht in den Compiler integriert ist, eine Unterklasse von etwas sein in den Compiler eingebaut). In NSObject sind viele nützliche Dinge integriert, die Sie nicht verpassen müssen.
Ein weiteres interessantes Beispiel ist UIView, die grundlegende "view" -Klasse in UIKit für die iOS-Entwicklung. Dies ist eine Klasse, die einerseits einer abstrakten Klasse ähnelt, die Funktionen deklariert, die Unterklassen ausfüllen sollen, aber auch für sich allein nützlich ist. Es verfügt über von UIKit gelieferte Unterklassen, die häufig unverändert verwendet werden. Der Entwickler installiert Unteransichten in einer Ansicht. Und es gibt oft von Entwicklern definierte Unterklassen, die häufig Kompositionen verwenden. Es gibt keine strengen Einzelregeln oder gar Regeln. Sie verwenden alles, was Ihren Anforderungen am besten entspricht.
quelle
type
, von denen jedes Nicht-Primitiv in Java erbtObject
, alles in JavaScript hat einen Prototyp, der mit endetObject
usw.[Ich riskiere eine freche Antwort auf die Titelfrage.]
Ja ... außer dass das Strategiemuster selbst die Vererbung verwendet. Das Strategiemuster arbeitet mit der Schnittstellenvererbung. Durch das Ersetzen der Vererbung durch Komposition + Strategie wird die Vererbung an einen anderen Ort verschoben. Trotzdem lohnt sich ein solcher Ersatz oft, weil er es ermöglicht, Hierarchien auseinanderzuhalten .
quelle
interface
Design gesehen, das mit Vererbung repariert wurde . Das versteckte Problem hierbei ist das inkonsistente, vom Morphing abhängige Verhalten der Implementierung. P, S. Vererbung und Schnittstelle schließen sich nicht gegenseitig ausinterface
Nein. Wenn eine Stockente andere Parameter zum Instanziieren benötigt als eine andere Ententyp, wäre es ein Albtraum, sie zu ändern. Sollten Sie auch in der Lage sein, eine Ente zu instanziieren? Es ist eher eine Stockente oder eine Mandarinenente oder eine andere Art von Enten, die Sie haben.
Außerdem möchte ich vielleicht eine Logik für die Typen, damit eine Typhierarchie besser ist.
Wenn die Wiederverwendung von Code für einige Funktionen problematisch wird, können wir sie erstellen, indem wir die Funktionen über den Konstruktor übergeben.
Auch hier kommt es wirklich auf Ihren Anwendungsfall an. Wenn Sie nur eine Ente und eine Stockente haben, ist eine Klassenhierarchie eine viel einfachere Lösung.
Hier ist ein Beispiel, in dem Sie das Strategiemuster verwenden möchten: Wenn Sie Kundenklassen haben und eine Abrechnungsstrategie (Schnittstelle) übergeben möchten, die eine Happy-Hour-Abrechnungsstrategie oder eine reguläre Abrechnungsstrategie sein kann, können Sie diese übergeben im Konstruktor. Auf diese Weise müssen Sie keine zwei verschiedenen Kundenklassen erstellen und benötigen keine Hierarchie. Sie haben nur die eine Kundenklasse.
quelle