Ich habe mich eine Weile für Designmuster interessiert und angefangen, "Head First Design Patterns" zu lesen. Ich begann mit dem ersten Muster, das als "Strategie" -Muster bezeichnet wurde. Ich ging das in den folgenden Bildern dargestellte Problem durch und versuchte zunächst, selbst eine Lösung vorzuschlagen, damit ich die Bedeutung des Musters wirklich erfassen konnte.
Meine Frage ist also, warum meine Lösung für das unten stehende Problem nicht gut genug ist. Was sind die guten / schlechten Punkte meiner Lösung gegenüber dem Muster? Was macht das Muster eindeutig zur einzig praktikablen Lösung?
MEINE LÖSUNG
Elternklasse: ENTE
<?php
class Duck
{
public $swimmable;
public $quackable;
public $flyable;
function display()
{
echo "A Duck Looks Like This<BR/>";
}
function quack()
{
if($this->quackable==1)
{
echo("Quack<BR/>");
}
}
function swim()
{
if($this->swimmable==1)
{
echo("Swim<BR/>");
}
}
function fly()
{
if($this->flyable==1)
{
echo("Fly<BR/>");
}
}
}
?>
Vererbungsklasse: MallardDuck
<?php
class MallardDuck extends Duck
{
function MallardDuck()
{
$this->quackable = 1;
$this->swimmable = 1;
}
function display()
{
echo "A Mallard Duck Looks Like This<BR/>";
}
}
?>
Vererbungsklasse: WoddenDecoyDuck
<?php
class WoddenDecoyDuck extends Duck
{
function woddendecoyduck()
{
$this->quackable = 0;
$this->swimmable = 0;
}
function display()
{
echo "A Wooden Decoy Duck Looks Like This<BR/>";
}
}
design-patterns
Imran Omar Bukhsh
quelle
quelle
Antworten:
Ihr Code wird kaputt gehen, wenn z. B. Enten mit anderen Geräuschen quaken. Der Boolesche Wert führt nur dazu, dass andere Boolesche Werte zu wirklich haarigen if-Anweisungen führen.
Das Strategiemuster ist jedoch einfach. Nehmen Sie in diesem Fall die Quacksalbermethode und platzieren Sie sie in einer eigenen Klasse oder Schnittstelle. Eine Schnittstelle in PHP wäre eine Klasse, die keine andere Implementierung als Methoden enthält, die Stubs sind (dh nichts passiert, wenn Sie sie aufrufen).
Auf diese Weise können Sie mehrere quackbare Implementierungen erstellen:
Und weil wir es strategisch gemacht haben, können wir weitere Arten von "Quacksalber" hinzufügen:
Gleiches gilt für Flug- und Schwimmmethoden für die Entenklasse. Die Art und Weise, wie Sie eine Duck-Klasse mit einem Quackable implementieren, sieht folgendermaßen aus (die Quackable-Schnittstelle wird über den Konstruktor bereitgestellt, so funktioniert die Abhängigkeitsinjektion):
Die Implementierung der Stockente und der Lockente ist einfach, da Sie nur angeben müssen, welche Art von Quacksalber die Ente tun soll:
Alles zu benutzen ist einfach:
Ich hoffe, das alles macht für Sie Sinn.
quelle
Erstens hat Ihr Problem nichts mit dem Strategiemuster zu tun. Die Idee des Strategiemusters besteht darin, die Verantwortung für ein bestimmtes Verhalten in eine andere Klasse einzubeziehen. Auf diese Weise können Sie zur Laufzeit verschiedene Verhaltensweisen in eine Instanz einbinden.
Jetzt funktioniert Ihre Lösung für das Szenario, in dem Sie sich befinden, recht gut, aber das Szenario ist nur eine Übung, die weit entfernt von Problemen der realen Welt ist.
Wenn Sie diese Technik verwenden, um große Projekte anzugehen, besteht die Gefahr, dass Sie 5-10 Vererbungsebenen erhalten, bei denen jede Unterklasse mit immer mehr Flags gekoppelt ist. Ein solcher Code ist äußerst fragil und ausführlich. Das Herumspielen mit dem internen Zustand aller Superklassen ist nicht gerade die OOP-Methode, um mit solchen Dingen umzugehen, da es die Trennung von Bedenken verwischt.
Eine Lösung mit Schnittstellen ist wesentlich sauberer, robuster und wird sich daher im Laufe der Zeit als wartbarer erweisen. Versuchen Sie nicht, eine allgemeine Allzweck-Basisente herzustellen, sondern machen Sie klare Abstraktionen der verschiedenen Aspekte verschiedener Enten. Ich habe kürzlich einen Blog-Beitrag zu diesem Thema verfasst, den Sie vielleicht hilfreich finden.
quelle
RuberBall
? Wird dasRuberDuck
auch eine leere Rollmethode haben?Ah, gute Frage. Ich denke nicht, dass es sehr empfehlenswert ist, Mitgliedsvariablen zu haben, die die Funktionalität umschalten. Das Problem bei dieser Lösung besteht hauptsächlich darin, dass Sie, da Sie die Methoden
fly
undquack
für dieDuck
Klasse verfügbar machen, anscheinend sagen, dass "Alle Enten könnenfly
/quack
". Was schlimmer ist , dass in Abhängigkeit von der Laufzeittyp der Ente Instanz i haben,fly
oderquack
kann oder auch gar nichts. Dies kann zu sehr verwirrendem Code führen.Wenn Sie im Beispiel des Buches eine Ente erwarten, die fliegen kann, können Sie eingeben (oder testen, da Sie eine dynamische Sprache verwenden), ob es sich um eine
Flyable
Ente handelt.Wenn ich jedes Mal, wenn ich eine Ente hatte, bevor ich sie anrufe,
quack
muss ich entscheiden, ob die fliegende Mitgliedsvariable auf true gesetzt ist oder nicht, was zu einer Menge Codeduplizierung führen würde.Die
Flyable
Schnittstelle hilft, Erwartungen zu etablieren. Ja, Ihre Standardmethodequack
prüft, ob dies der Fall sein sollte oder nicht, aber ich als Anrufer habe keine Garantie dafür, dass das Anrufen sogar sicher istquack
. Denken Sie auch an das Szenario mit dem,RubberDucky
das eher quietschen als quaken sollte. Wenn Sie diequack
Methode überschreiben , müssen Sie wissen, um die$quackable
Mitgliedsvariable zu überprüfen , bevor Sie Maßnahmen ergreifen.Etwas anderes, das meiner Meinung nach diese Lösung unklar macht, ist, dass es schwierig ist, Erwartungen an Eingabetypen zu setzen, da Sie eine dynamische Sprache wie PHP verwenden. (Nicht, dass daran etwas falsch ist, aber eine statisch typisierte Sprache könnte das Verständnis erleichtern.)
Ich hoffe das hilft und macht Sinn.
quelle
Ihr Code wird schnell unordentlich, wenn Sie 10 verschiedene Arten von Enten haben, die alle unterschiedliche Kombinationen von beispielsweise 3 verschiedenen Varianten von Fliegen, Quaken und Schwimmen haben.
Persönlich stellte ich fest, dass der wahre Wert des Strategiemusters klar wurde, als ich anfing, Komponententests zu schreiben und Abhängigkeitsinjektion zu verwenden. Wenn Sie es nicht verwenden, haben Sie große Probleme beim Verwalten von Abhängigkeiten und beim Erstellen von Objekten in Ihren Tests.
Sie werden in Situationen geraten, in denen das Erstellen einer Ente kompliziert wird. Dann möchten Sie möglicherweise einen Komponententest für einige der Quacksalber- oder Flugmethoden schreiben, aber Sie können dies nicht tun, ohne in Ihrem Test eine Ente zu erstellen.
quelle
Ein boolescher Zustand zum Speichern, ob Enten quaken / fliegen können, ist eine sehr spezifische Lösung für das spezielle Problem, mit dem Sie in Ihrem Klassendesign konfrontiert sind, und kann möglicherweise nicht auf andere Fälle angewendet werden, in denen das Strategiemuster angemessen ist.
Ich denke, Ihr "quackbarer" Ansatz macht den Code etwas komplizierter. Es ist eine Sache mehr, die Sie und Benutzer Ihrer Klasse beachten müssen, als wenn Sie ein Strategiemuster verwenden würden.
Da Ihr Beispielcode keine Beispiele dafür zeigt, wie Sie die Methoden "Quacksalber" und "Fliegen" überschreiben würden, bemerken Sie keinen großen Vorteil der Verwendung des Strategiemusters - Reduzierung der Codeduplizierung. Was würden Sie bei einem Dutzend verschiedener Entenklassen tun, die untereinander drei verschiedene "Fliegen" -Verhalten haben? Wenn Sie Ihren Ansatz verwenden, werden Sie wahrscheinlich feststellen, dass Sie den duplizierten Code ausschneiden und einfügen und dann möglicherweise in statische "Hilfs" -Klassen extrahieren. Das Strategiemuster ist viel sauberer.
quelle
Eine schöne visuelle Demo sagt immer mehr als tausend Worte.
John Lindquist macht einen großartigen Job, um Screencasts über verschiedene Designmuster aufzunehmen. Sie können seine 50 Cent über dieses bestimmte Muster (und vieles mehr) auf seinem Blog finden
quelle