Ich habe einen Programmierartikel gelesen, in dem das Decorator-Muster erwähnt wurde. Ich habe eine Weile lang programmiert, aber ohne irgendeine formale Ausbildung oder Schulung, aber ich versuche, etwas über die Standardmuster und dergleichen zu lernen.
Also habe ich den Decorator nachgeschlagen und einen Wikipedia-Artikel darüber gefunden. Ich verstehe jetzt das Konzept des Decorator-Musters, war aber etwas verwirrt über diese Passage:
Betrachten Sie als Beispiel ein Fenster in einem Fenstersystem. Um das Scrollen des Fensterinhalts zu ermöglichen, möchten wir ihm gegebenenfalls horizontale oder vertikale Bildlaufleisten hinzufügen. Angenommen, Fenster werden durch Instanzen der Window-Klasse dargestellt, und diese Klasse verfügt über keine Funktionalität zum Hinzufügen von Bildlaufleisten. Wir könnten eine Unterklasse von ScrollingWindow erstellen, die diese bereitstellt, oder wir könnten einen ScrollingWindowDecorator erstellen, der diese Funktionalität zu vorhandenen Window-Objekten hinzufügt. Zu diesem Zeitpunkt wäre jede Lösung in Ordnung.
Nehmen wir nun an, wir möchten auch die Möglichkeit haben, unseren Fenstern Rahmen hinzuzufügen. Auch hier hat unsere ursprüngliche Window-Klasse keine Unterstützung. Die ScrollingWindow-Unterklasse wirft nun ein Problem auf, da sie effektiv eine neue Art von Fenster erstellt hat. Wenn wir allen Fenstern Rahmenunterstützung hinzufügen möchten, müssen wir Unterklassen WindowWithBorder und ScrollingWindowWithBorder erstellen. Offensichtlich wird dieses Problem mit jeder neuen Funktion, die hinzugefügt wird, noch schlimmer. Für die Decorator-Lösung erstellen wir einfach einen neuen BorderedWindowDecorator. Zur Laufzeit können wir vorhandene Fenster mit dem ScrollingWindowDecorator oder dem BorderedWindowDecorator oder beiden nach Belieben dekorieren.
OK, wenn sie sagen, dass allen Fenstern Rahmen hinzugefügt werden sollen, warum nicht einfach der ursprünglichen Fensterklasse Funktionen hinzufügen, um die Option zuzulassen? Untergeordnete Klassen dienen meiner Ansicht nach nur dazu, einer Klasse bestimmte Funktionen hinzuzufügen oder eine Klassenmethode zu überschreiben. Wenn ich allen vorhandenen Objekten Funktionen hinzufügen müsste, warum sollte ich dann nicht einfach die Superklasse ändern?
Es gab eine andere Zeile im Artikel:
Das Dekorationsmuster ist eine Alternative zur Unterklasse. Unterklassen fügen Verhalten zur Kompilierungszeit hinzu, und die Änderung wirkt sich auf alle Instanzen der ursprünglichen Klasse aus. Dekorieren kann zur Laufzeit für einzelne Objekte ein neues Verhalten bereitstellen.
Ich verstehe nicht, wo sie sagen "... die Änderung wirkt sich auf alle Instanzen der ursprünglichen Klasse aus" - wie ändert die Unterklasse die übergeordnete Klasse? Ist das nicht der springende Punkt bei der Unterklassifizierung?
Ich gehe davon aus, dass der Artikel, wie viele Wikis, einfach nicht klar geschrieben ist. Ich kann den Nutzen des Decorators in der letzten Zeile sehen - "... zur Laufzeit neues Verhalten für einzelne Objekte bereitstellen."
Ohne dieses Muster gelesen zu haben, hätte ich, wenn ich das Verhalten zur Laufzeit für einzelne Objekte ändern müsste, wahrscheinlich einige Methoden in die Ober- oder Unterklasse eingebaut, um dieses Verhalten zu aktivieren / deaktivieren. Bitte helfen Sie mir, die Nützlichkeit des Dekorateurs wirklich zu verstehen, und warum ist mein Nachwuchsdenken fehlerhaft?
Antworten:
Das Dekorationsmuster ist eines, das die Komposition gegenüber der Vererbung bevorzugt [ein weiteres OOP-Paradigma, über das man etwas lernen kann]
Der Hauptvorteil des Dekorationsmusters gegenüber Unterklassen besteht darin, dass mehr Mix & Match-Optionen möglich sind. Wenn Sie zum Beispiel 10 verschiedene Verhaltensweisen haben, die ein Fenster haben kann, bedeutet dies, dass Sie mit Unterklassen jede unterschiedliche Kombination erstellen müssen, was auch zwangsläufig eine Menge Wiederverwendung von Code mit sich bringt.
Was passiert jedoch, wenn Sie ein neues Verhalten hinzufügen möchten?
Mit dem Decorator fügen Sie einfach eine neue Klasse hinzu, die dieses Verhalten beschreibt, und das ist es - mit dem Muster können Sie dies effektiv einfügen, ohne den Rest des Codes zu ändern.
Mit der Subklassifizierung haben Sie einen Albtraum in den Händen.
Eine Frage, die Sie gestellt haben, war: "Wie ändert Unterklassen die übergeordnete Klasse?" Es ist nicht so, dass es die Elternklasse ändert. Wenn es sich um eine Instanz handelt, bedeutet dies jedes Objekt, das Sie "instanziiert" haben [wenn Sie Java oder C # verwenden, beispielsweise mit dem
new
Befehl]. Wenn Sie diese Änderungen zu einer Klasse hinzufügen, haben Sie keine andere Wahl, als dass diese Änderung vorhanden ist, auch wenn Sie sie nicht wirklich benötigen.Alternativ können Sie alle seine Funktionen in einer einzelnen Klasse zusammenfassen, indem Sie sie über Flags aktivieren / deaktivieren. Dies führt jedoch zu einer einzelnen Klasse, die mit dem Wachstum Ihres Projekts immer größer wird.
Es ist nicht ungewöhnlich, Ihr Projekt auf diese Weise zu starten und es in ein Dekorationsmuster umzugestalten, sobald Sie eine effektive kritische Masse erreicht haben.
Ein interessanter Punkt, der angesprochen werden sollte: Sie können dieselbe Funktionalität mehrmals hinzufügen. So können Sie zum Beispiel ein Fenster mit doppelter, dreifacher oder einer beliebigen Anzahl oder Begrenzung nach Ihren Wünschen haben.
Der Hauptpunkt des Musters besteht darin, Laufzeitänderungen zu ermöglichen: Möglicherweise wissen Sie nicht, wie das Fenster aussehen soll, bis das Programm ausgeführt wird. Auf diese Weise können Sie es problemlos ändern. Zugegeben, dies kann über die Unterklasse erfolgen, ist aber nicht so schön.
Und schließlich können Klassen, die Sie möglicherweise nicht bearbeiten können, Funktionen hinzugefügt werden - beispielsweise in versiegelten / finalen Klassen oder in Klassen, die von anderen APIs bereitgestellt werden
quelle
Berücksichtigen Sie die Möglichkeiten, Bildlaufleisten und Rahmen mit Unterklassen hinzuzufügen. Wenn Sie alle Möglichkeiten nutzen möchten, erhalten Sie vier Klassen (Python):
In
WindowWithScrollBarAndBorder.draw()
wird das Fenster nun zweimal gezeichnet, und beim zweiten Mal wird die bereits gezeichnete Bildlaufleiste möglicherweise überschrieben, je nach Implementierung. Ihr abgeleiteter Code ist also eng an die Implementierung einer anderen Klasse gekoppelt, und Sie müssen sich jedes Mal darum kümmern, wenn Sie dasWindow
Verhalten der Klasse ändern . Eine Lösung wäre, den Code aus den übergeordneten Klassen in die abgeleiteten Klassen zu kopieren und an die Anforderungen der abgeleiteten Klassen anzupassen. Jede Änderung in einer übergeordneten Klasse muss jedoch erneut kopiert und angepasst werden, sodass die abgeleiteten Klassen erneut eingefügt werden eng an die Basisklasse gekoppelt (über die Notwendigkeit von Copy-Paste-Adjust). Ein weiteres Problem ist, dass Sie jede Klasse verdoppeln müssen, wenn Sie eine weitere Eigenschaft benötigen, die ein Fenster haben kann oder nicht:Das heißt für eine Menge voneinander unabhängiger Merkmale f mit | f | Als Anzahl der Features müssen Sie 2 ** | f | definieren Klassen, zB. Wenn Sie 10 Funktionen haben, erhalten Sie 1024 eng begrenzte Klassen. Wenn Sie das Decorator-Muster verwenden, erhält jedes Feature seine eigene unabhängige und lose gekoppelte Klasse, und Sie haben nur 1 + | f | Klassen (das ist 11 für das obige Beispiel).
quelle
Ich bin kein Experte für dieses spezielle Muster, aber meines Erachtens kann das Decorator-Muster auf Klassen angewendet werden, die Sie möglicherweise nicht modifizieren oder unterklassifizieren können (sie sind möglicherweise nicht Ihr Code und werden beispielsweise versiegelt) ). Was ist in Ihrem Beispiel, wenn Sie die Window-Klasse nicht geschrieben haben, sondern nur verbrauchen? Solange die Window-Klasse eine Schnittstelle hat und Sie gegen diese Schnittstelle programmieren, kann Ihr Decorator dieselbe Schnittstelle verwenden, aber die Funktionalität erweitern.
Das Beispiel , das Sie erwähnen , ist versichert hier ganz ordentlich:
http://www.oodesign.com/decorator-pattern.html
quelle