{"id":274,"date":"2015-04-05T06:39:33","date_gmt":"2015-04-05T04:39:33","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=274"},"modified":"2015-09-06T09:53:48","modified_gmt":"2015-09-06T07:53:48","slug":"offen-und-geschlossen","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/offen-und-geschlossen\/","title":{"rendered":"Offen und geschlossen!?"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>Wie gerne w\u00fcrden wir Coder auf der gr\u00fcnen Wiese entwickeln &#8211; und damit meine ich nicht in der grellen Sonne. Aber immer wieder geht es darum, bereits vorhandenen Code zu ver\u00e4ndern. St\u00e4ndig \u00e4ndern sich die Anforderungen: hier noch etwas mehr Features, dort noch etwas weniger Bugs usw.<\/p>\n<p>Im Artikel <a title=\"Aus Prinzip nur eine Verantwortlichkeit\" href=\"http:\/\/invidit.de\/blog\/aus-prinzip-nur-eine-verantwortlichkeit\/\">Aus Prinzip nur eine Verantwortlichkeit<\/a> haben wir uns das erste der SOLID-Prinzipien angeschaut. Heute geht es um das zweite Design Prinzip.<\/p>\n<p>&nbsp;<\/p>\n<h2>Open-Closed-Principle (OCP)<\/h2>\n<p>Wie auch bei Wikipedia unter <a title=\"https:\/\/de.wikipedia.org\/wiki\/Open-Closed-Prinzip\" href=\"https:\/\/de.wikipedia.org\/wiki\/Open-Closed-Prinzip\" target=\"_blank\">https:\/\/de.wikipedia.org\/wiki\/Open-Closed-Prinzip<\/a> zu lesen ist, besagt das OCP: Klassen, Methoden, etc. sollen sowohl <strong>offen<\/strong> (f\u00fcr Erweiterungen), als auch <strong>geschlossen<\/strong> (f\u00fcr \u00c4nderungen) sein.<\/p>\n<p>&nbsp;<\/p>\n<h2>Erl\u00e4uterung des Prinzips<\/h2>\n<p>Offen <strong>und<\/strong> geschlossen &#8211; wie soll das denn aussehen? Mal angenommen, ich packe meinen gesamten Code in eine einzige Klasse mit vielen gro\u00dfen Methoden (so etwas soll es tats\u00e4chlich geben). Will oder muss ich nun einer \u00c4nderung an der Klasse vornehmen, weil ich zum Beispiel ein Feature hinzuf\u00fcge oder einen Bug behebe, wer sagt mir dann, dass ich keine ungewollte Verhaltens\u00e4ndeurng der Klasse herbeif\u00fchre? Habe ich wirklich die gesamte Klasse im Blick und durchschaue die Abh\u00e4ngigkeiten zwischen den Methoden und Variablen?<\/p>\n<p>Wie oben bereits erw\u00e4hnt kommen wir um \u00c4nderungen an bestehendem Code nicht herum. Aber es ist doch w\u00fcnschenswert, ein neues Verhalten ganz leicht erg\u00e4nzen zu k\u00f6nnen. Oder Ver\u00e4nderung von bestehendem Verhalten an einer sehr kleinen Klasse mit wenigen Methoden vornehmen zu k\u00f6nnen. Aber wie muss der Code gestaltet werden, damit ich dies erreichen kann?<\/p>\n<p>Zun\u00e4chst m\u00f6chte ich klarstellen, dass das OCP insbesondere dann seine Vorteile entfaltet, wenn der betroffene Code h\u00e4ufig ge\u00e4ndert wird. Woher wei\u00df ich das jetzt im Voraus? Gar nicht. Daher schreibe ich neuen Code so einfach wie m\u00f6glich, so dass er exakt die Anforderung erf\u00fcllt &#8211; nicht mehr und nicht weniger. Auch die erste \u00c4nderung am Code nehme ich mit derselben Idee vor. Sobald ich jedoch die zweite \u00c4nderung vornehme, vielleicht sogar nach kurzer Zeit, kann ich erahnen, dass dieser Code h\u00e4ufigen \u00c4nderungen unterliegen wird. Genau dann \u00e4ndere ich den Code so, dass zuk\u00fcnftige Erweiterungen sehr leicht m\u00f6glich sind.<\/p>\n<p>Dazu eignen sich zum Beispiel diese beiden Entwurfsmuster.<\/p>\n<ol>\n<li>Template Method Pattern<\/li>\n<li>Strategy Pattern<\/li>\n<\/ol>\n<p>Beide Entwurfsmuster sind auch in <sup>[b.]<\/sup> beschrieben und werden hier nur kurz skizziert.<\/p>\n<p>Das Verhaltensmuster <em>Schablonenmethode<\/em> (Template Method) stellt im Rahmen der Klassenvererbung Einstiegspunkte bereit, um Verhalten in einer abgeleiteten Klasse zu erg\u00e4nzen. So wird in einer (abstrakten) Klassen eine Methode implementiert, welche innerhalb der Verarbeitung eine oder mehrere abstrakte Methoden aufruft. Diese Methoden k\u00f6nnen dann in der Kindklasse mit Leben gef\u00fcllt und somit die Verarbeitung mit neuem Verhalten angereichert werden, ohne die Vaterklasse \u00e4ndern zu m\u00fcssen.<\/p>\n<p>Das Verhaltensmuster <em>Strategie<\/em> (Strategy) erm\u00f6glicht es, einer Methode ihr Verhalten mitzugeben. Anstelle der x-ten If-Abfrage oder dem weiteren switch-case wird einfach eine Methode einer anderen Klasse aufgerufen &#8211; wobei hier gegen ein Interface programmiert wird. Die Implementierung wird dann jeweils abh\u00e4ngig vom Kontext mitgegeben oder bereitgestellt. Dadurch kann ein neues Verhalten als weitere Implementierung des Interfaces erfolgen, ohne die urspr\u00fcngliche Klasse \u00e4ndern zu m\u00fcssen.<\/p>\n<p>Dies sind nur zwei M\u00f6glichkeiten, eine Klasse oder Methode offen f\u00fcr neues Verhalten zu gestalten. Geschlossen gegen\u00fcber Ver\u00e4nderungen bleiben diese Klassen und Methoden ebenfalls, da die Verhaltens\u00e4nderung an anderer Stelle vorgenommen werden kann &#8211; n\u00e4mlich in einer separaten Klasse.<\/p>\n<p>&nbsp;<\/p>\n<h2>Unsere Erfahrung mit dem OCP<\/h2>\n<p>Das OCP haben wir zum Beispiel in dem nie ver\u00f6ffentlichten Kultspiel <em>Magic Conflict<\/em> (ein rundenbasiertes Strategiespiel) eingesetzt. Der Spieler kann hier verschiedene Aktionen durchf\u00fchren. Diese Aktionen h\u00e4tten sicherlich alle mit mehreren If-Bl\u00f6cken oder switch\/case-Anweisungen implementiert werden k\u00f6nnen. Da uns bei der Entwicklung recht schnell klar war, dass wir die Aktionen zeitnah erweitern wollen, haben wir das <em>Template Method Pattern<\/em> angewendet. Damit gibt es eine abstrakte Basisklasse <em>PlayerAction<\/em> mit unter anderem dieser Methode:<\/p>\n<pre class=\"lang:c# decode:true \">public void Execute()\r\n{\r\n\tCheckValidity();\r\n\tExecuteImplementation();\r\n\tthis.Executed = true;\r\n}\r\n\r\nprotected abstract void ExecuteImplementation();<\/pre>\n<p>In der Methode <em>CheckValidity()<\/em> wird ein injizierter Validator befragt, ob diese Aktion jetzt g\u00fcltig ist (zum Beispiel alle Ressourcen verf\u00fcgbar oder das angegriffene Ziel zul\u00e4ssig ist). Dann wird die eigentliche Aktion durchgef\u00fchrt, indem die abstrakte Methode <em>ExecuteImplementation()<\/em> aufgerufen wird, welche in den konkreten Aktionen (je Aktion eine Klasse) implementiert wird.<\/p>\n<p>Auch das <em>Strategy Pattern<\/em> haben wir an verschiedenen Stellen in unserem Code angewendet und sind der Meinung, dass das OCP es sehr einfach m\u00f6glich macht, den vorhandenen Code um neue Funktionalit\u00e4t zu erg\u00e4nzen, ohne die bestehenden Klasse \u00f6ffnen (also \u00e4ndern) zu m\u00fcssen.<\/p>\n<p>Wenn abzusehen ist, dass der vorliegende Code h\u00e4ufig erweitert werden wird, sprechen wir eine klar Empfehlung f\u00fcr das OCP aus.<\/p>\n<p>&nbsp;<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>In diesem Artikel haben wir einen Blick auf das zweite der SOLID-Prinzipien, n\u00e4mlich das Open-Closed-Principle, geworfen. Methoden, Klassen und Module so zu gestalten, dass neues Verhalten leicht hinzugef\u00fcgt werden kann, ohne vorhandene Klasse \u00e4ndern zu m\u00fcssen, reduziert das Potenzial, bestehenden Code kaputt zu machen. Und so k\u00f6nnen wir doch ein St\u00fcck auf der gr\u00fcnen Wiese programmieren \ud83d\ude09<\/p>\n<p>&nbsp;<\/p>\n<p>Viel Spa\u00df beim Anwenden.<\/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<ol style=\"list-style-type: lower-alpha;\">\n<li id=\"gs_cit0\" class=\"gs_citr\" tabindex=\"0\">Martin, Robert C. <i>Clean Code-Refactoring, Patterns, Testen und Techniken f\u00fcr sauberen Code: Deutsche Ausgabe<\/i>. MITP-Verlags GmbH &amp; Co. KG, 2013.<\/li>\n<li class=\"gs_citr\" tabindex=\"0\">\n<div id=\"gs_cit0\" class=\"gs_citr\" tabindex=\"0\">Gamma, Erich, et al. <i>Design patterns: elements of reusable object-oriented software<\/i>. Pearson Education, 1994.<\/div>\n<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. Wie gerne w\u00fcrden wir Coder auf der gr\u00fcnen Wiese entwickeln &#8211; und damit meine ich nicht in der grellen Sonne. Aber immer wieder geht es darum, bereits vorhandenen Code zu ver\u00e4ndern. St\u00e4ndig \u00e4ndern sich die Anforderungen: hier noch etwas mehr Features, dort noch etwas weniger Bugs usw. Im Artikel Aus Prinzip nur eine [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":291,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[31,44,34,33,32],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/274"}],"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=274"}],"version-history":[{"count":10,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/274\/revisions"}],"predecessor-version":[{"id":288,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/274\/revisions\/288"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/291"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=274"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=274"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=274"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}