{"id":1016,"date":"2016-04-19T16:15:05","date_gmt":"2016-04-19T14:15:05","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=1016"},"modified":"2020-09-21T11:53:39","modified_gmt":"2020-09-21T09:53:39","slug":"nicht-nur-huebsch-dekoriert-sondern-auch-funktional-erweitert","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/nicht-nur-huebsch-dekoriert-sondern-auch-funktional-erweitert\/","title":{"rendered":"Nicht nur h\u00fcbsch dekoriert, sondern auch funktional erweitert"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>In unserem letzten Artikel zur Serie \u00fcber Entwurfsmuster haben wir uns die sog. <em>Erzeugungsmuster<\/em> angeschaut. Mit diesem Artikel wechseln wir in die Kategorie der <em>Strukturmuster<\/em> und werfen einen Blick auf den <strong>Dekorierer<\/strong> (engl. Decorator), ebenfalls aus der Sammlung der Gang of Four.<\/p>\n<p>&nbsp;<\/p>\n<h1>Wie funktioniert der Dekorierer?<\/h1>\n<p>Ebenso wie das traute Heim dekoriert und damit h\u00fcbscher gestaltet wird (naja, die Aussage ist sicherlich nicht allgemeing\u00fcltig&#8230;) k\u00f6nnen auch Implementierungen von Schnittstellen oder abstrakten Klassen dekoriert und dadurch um weitere Funktionalit\u00e4t erg\u00e4nzt werden. Machen wir es direkt konkret und schauen uns ein Beispiel an &#8211; einen Geschirrsp\u00fcler.<\/p>\n<pre class=\"lang:java decode:true \">public interface Dishwasher {\n    void load(List&lt;Dishes&gt; dishes);\n    void wash();\n    List&lt;Dishes&gt; clearOut();\n}<\/pre>\n<p>Der gemeinsame Nenner in allen Preisklassen von Geschirrsp\u00fclern ist vermutlich in etwa durch diese Schnittstelle ausgedr\u00fcckt: ich kann meinen Kram in den Geschirrsp\u00fcler einr\u00e4umen, ihn einschalten und im besten Fall mein sauberes Geschirr am Ende der Laufzeit wieder raus nehmen.<\/p>\n<p>Bauen wir uns ein sehr einfaches Modell, das genau nur diese Funktionen erf\u00fcllt. Die modernen Ger\u00e4te sind sicherlich mit dem Internet verbunden und schauen \u00fcber die Webcam des Laptops automatisch nach, ob noch gen\u00fcgend sauberes Geschirr im Schrank ist, aber nun gut.<\/p>\n<pre class=\"lang:java decode:true \">public class NoNameSimpleDishwasher implements Dishwasher {\n    private List&lt;Dishes&gt; loadedDishes;\n\n    public NoNameSimpleDishwasher() {\n        this.loadedDishes = new ArrayList&lt;&gt;();\n    }\n\n    @Override\n    public void load(List&lt;Dishes&gt; dishes) {\n        this.loadedDishes = dishes;\n    }\n\n    @Override\n    public void wash() {\n        \/\/ make everything pretty clean\n    }\n\n    @Override\n    public List&lt;Dishes&gt; clearOut() {\n        ArrayList&lt;Dishes&gt; dishes = new ArrayList&lt;&gt;(loadedDishes);\n        this.loadedDishes.clear();\n        return dishes;\n    }\n}<\/pre>\n<p>Bis hier hin noch nichts Spannendes. Lassen wir den Geschirrsp\u00fcler nun laufen, passiert augenscheinlich nichts.<\/p>\n<pre class=\"lang:java decode:true \">public class Main {\n    public static void main(String...args) {\n\n        System.out.println(\"Original dishwasher:\");\n        Dishwasher dishwasher = new NoNameSimpleDishwasher();\n        dishwasher.wash();\n    }\n}<\/pre>\n<p>Die Konsole sieht dann wie folgt aus:<\/p>\n<pre class=\"lang:default decode:true \">Original dishwasher:<\/pre>\n<p>W\u00e4re es nicht super, wenn der Geschirrsp\u00fcler zumindest auf der Konsole Bescheid sagt, was er gerade so macht? Kein Problem, \u00e4ndern wir doch unsere Implementierung und f\u00fcgen diese Funktionalit\u00e4t hinzu. Was ist aber, wenn wir dies nicht k\u00f6nnen, z.B. da es eine Klasse aus einer fremden Bibliothek ist oder wir die Klasse nicht \u00e4ndern wollen, weil sie so umfangreich, kompliziert und fehleranf\u00e4llig ist?<\/p>\n<p>D.h. wir haben den Wunsch, dem Geschirrsp\u00fcler (also unserer Implementierung) neue Funktionalit\u00e4ten hinzuf\u00fcgen, ohne die Klasse selbst zu ver\u00e4ndern. Dazu bauen wir einen <strong>Dekorierer<\/strong>.<\/p>\n<pre class=\"lang:java decode:true \">public class DishwasherLoggingDecorator implements Dishwasher {\n    private Dishwasher decoratedDishwasher;\n\n    public DishwasherLoggingDecorator(Dishwasher dishwasher) {\n        this.decoratedDishwasher = dishwasher;\n    }\n\n    @Override\n    public void load(List&lt;Dishes&gt; dishes) {\n        this.decoratedDishwasher.load(dishes);\n        System.out.println(\"Dishes loaded\");\n    }\n\n    @Override\n    public void wash() {\n        this.decoratedDishwasher.wash();\n        System.out.println(\"Dishes washed\");\n    }\n\n    @Override\n    public List&lt;Dishes&gt; clearOut() {\n        System.out.println(\"Clear out sparkling dishes\");\n        return this.decoratedDishwasher.clearOut();\n    }\n}<\/pre>\n<p>Ein neuer Geschirrsp\u00fcler? Nicht ganz. Die Idee beim Dekorierer ist, dass dieser zun\u00e4chst dieselbe Schnittstelle implementiert, wie die zu dekorierende Klasse. Per Konstruktor wird eine Implementierung der Schnittstelle mitgegeben, an welche sp\u00e4ter die tats\u00e4chliche Arbeit <strong>delegiert<\/strong> wird. In den implementierten Methoden der Schnittstelle wird immer dieselbe Methode auf der im Konstruktor gemerkten Implementierung aufgerufen und zus\u00e4tzlich &#8211; und das ist der Trick an der Sache &#8211; weiterer Code ausgef\u00fchrt. In unserem Beispiel erfolgt eine Ausgabe auf der Konsole.<\/p>\n<p>Lassen wir nun den original Geschirrsp\u00fcler sowie den LoggingDecorator laufen, sieht die Ausgabe auf der Konsole so aus:<\/p>\n<pre class=\"lang:default decode:true \">Original dishwasher:\nDecorated dishwasher: Dishes washed<\/pre>\n<p>Unsere Main-Methode ist dazu in der Form erweitert:<\/p>\n<pre class=\"lang:java decode:true \">public class Main {\n    public static void main(String...args) {\n\n        System.out.println(\"Original dishwasher:\");\n        Dishwasher dishwasher = new NoNameSimpleDishwasher();\n        dishwasher.wash();\n\n        System.out.println(\"Decorated dishwasher:\");\n        dishwasher = new DishwasherLoggingDecorator(new NoNameSimpleDishwasher());\n        dishwasher.wash();\n    }\n}<\/pre>\n<p>Hier sehen wir, dass der LoggingDecorator per Konstruktor die tats\u00e4chliche Implementierung erh\u00e4lt, die sp\u00e4ter die Arbeit machen soll, an die also die Aufrufe delegiert werden.<\/p>\n<p>Da der Dekorierer dieselbe Schnittstelle implementiert \u00e4ndert sich der Aufruf nicht. Der Aufrufende wei\u00df ggf. gar nicht, dass es sich bei der Implementierung um einen Dekorierer handelt. Nur bei der Erzeugung muss nat\u00fcrlich einmalig festgelegt werden, welcher Geschirrsp\u00fcler im Folgenden benutzt werden soll.<\/p>\n<p>&nbsp;<\/p>\n<h1>Wann ist der Dekorierer sinnvoll anzuwenden?<\/h1>\n<p>Im Abschnitt oben haben wir bereits erw\u00e4hnt, das dieses Strukturmuster dann angewendet werden kann, wenn eine vorhandene Klasse um neue Funktionalit\u00e4t erweitert werden soll, die Klasse selbst aber nicht ge\u00e4ndert werden kann, darf oder will. Ein Logging ist hier eine klassische Aufgabe f\u00fcr einen Dekorierer. Einige von euch denken nun vermutlich &#8222;Moment, das w\u00fcrde ich doch per AOP l\u00f6sen&#8220;. Ja stimmt, Aspekt-Orientierte Programmierung ist ein anderer Ansatz, solche Anforderungen umzusetzen.<\/p>\n<p>Wer das Muster nun weiter denkt, stellt vielleicht die Frage &#8222;Kann ich eigentlich einen Dekorierer dekorieren?&#8220;. Wir haben hier keine statische Ableitungshierarchie geschaffen, sondern \u00fcber die jeweilige Implementierung der entsprechenden Schnittstelle die M\u00f6glichkeit, einzelne Funktionen in separate Dekorierer zu strecken und diese dann je nach Bedarf ineinander zu verschachteln. Jeder Dekorierer reicht die Aufrufe an den n\u00e4chste Dekorierer weiter bis ganz innen die eigentliche Verarbeitung erfolgt. Damit lassen sich zur Laufzeit Entscheidungen treffen, welche Funktionen gerade ben\u00f6tigt werden und die Dekorierer entsprechend aufbauen bzw. zusammenstecken.<\/p>\n<p>Ebenso wie das Hinzuf\u00fcgen von Funktionalit\u00e4t ist auch das Entfernen von Funktionalit\u00e4t m\u00f6glich. Statt den Aufruf weiter zu delegieren kann eine eigene oder gar keine Implementierung durchlaufen werden.<\/p>\n<p>Insbesondere mit steigender Anzahl von verwendeten Dekorieren ist die Suche nach einem auftretenden Fehler dadurch erschwert, dass die Ursache in einem der Dekorierer liegen kann, aber in welchem? Die Komplexit\u00e4t steigt und die Verwendung ist m\u00f6glicherweise unklar: wann nutzte ich welchen Dekorierer. Habt ihr schon einmal mit den Java-IO-Streams und Readern gearbeitet? Diese sind nach dem Decorator Pattern aufgebaut. Das wird irgendwann un\u00fcbersichtlich.<\/p>\n<p>&nbsp;<\/p>\n<h1>Zusammenfassung?<\/h1>\n<p>Wir haben uns in diesem Artikel ein <em>Strukturmuster<\/em> aus der Liste der Entwurfsmuster angeschaut, mit dem eine bestehende Klasse um neue Funktionalit\u00e4t erweitert werden kann, ohne die Klasse selbst zu ver\u00e4ndern. Damit machen wir am bestehenden Code nichts kaputt und alle bisherigen Aufrufe funktionieren weiterhin so falsch oder richtig wie zuvor \ud83d\ude42<\/p>\n<p>&nbsp;<\/p>\n<p>Welchem Anlass k\u00f6nnt ihr euch noch vorstellen, euren Code zu dekorieren?<\/p>\n<p>&nbsp;<\/p>\n<p>Eure Spa\u00df-Coder<\/p>\n<p>&nbsp;<\/p>\n<p>Dieser Artikel basiert neben unseren Erfahrungen auf den Ausf\u00fchrungen aus:<\/p>\n<ul>\n<li>https:\/\/de.wikipedia.org\/wiki\/Decorator<\/li>\n<li>https:\/\/de.wikipedia.org\/wiki\/Aspektorientierte_Programmierung<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. In unserem letzten Artikel zur Serie \u00fcber Entwurfsmuster haben wir uns die sog. Erzeugungsmuster angeschaut. Mit diesem Artikel wechseln wir in die Kategorie der Strukturmuster und werfen einen Blick auf den Dekorierer (engl. Decorator), ebenfalls aus der Sammlung der Gang of Four. &nbsp; Wie funktioniert der Dekorierer? Ebenso wie das traute Heim dekoriert [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1270,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[143,144,130,145],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1016"}],"collection":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/comments?post=1016"}],"version-history":[{"count":18,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1016\/revisions"}],"predecessor-version":[{"id":1049,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/1016\/revisions\/1049"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/1270"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=1016"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=1016"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=1016"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}