{"id":334,"date":"2015-04-26T08:32:44","date_gmt":"2015-04-26T06:32:44","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=334"},"modified":"2015-09-06T09:53:14","modified_gmt":"2015-09-06T07:53:14","slug":"teile-und-herrsche","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/teile-und-herrsche\/","title":{"rendered":"Teile und Herrsche"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>Wer von euch hat schon einmal ein Interface implementiert und findet anschlie\u00dfend ein oder mehrere leere Methoden oder Methoden mit <em>throw new NotImplementedException()<\/em> in seiner neuen Klasse wieder? Warum? Weil einfach zu viele Methoden im Interface vorgesehen sind, die mit der eigentlichen Aufgabe gar nichts zu tun haben. Hier werden wir immer wieder dazu gezwungen, viele Methoden in der Implementierung anbieten zu m\u00fcssen, was auf der anderen Seite auch die Verwendung des Interfaces auf Grund dieser hohen Anzahl von Methoden nicht gerade einfach macht. Schauen wir uns daher das vierte Prinzip der SOL<strong>I<\/strong>D-Prinzipien an.<\/p>\n<p>&nbsp;<\/p>\n<h2 id=\"firstHeading\" lang=\"de\">Interface Segregation Principle (ISP)<\/h2>\n<blockquote><p>\u201eClients sollten nicht dazu gezwungen werden, von Interfaces abzuh\u00e4ngen, die sie nicht verwenden.\u201c<\/p>\n<p>\u2013 <span class=\"Person\">Robert C. Martin<\/span>: The Interface Segregation Principle (Quelle: <a href=\"https:\/\/de.wikipedia.org\/wiki\/Prinzipien_objektorientierten_Designs#Interface-Segregation-Prinzip\" target=\"_blank\">Wikipedia<\/a>)<\/p><\/blockquote>\n<p>&nbsp;<\/p>\n<h2>Erl\u00e4uterung des Prinzips<\/h2>\n<p>Im Wesentlichen geht es beim ISP mal wieder um eine gute Namensgebung und der Umsetzung von <strong>einer<\/strong> konkreten Anforderung. Ein schlecht gew\u00e4hlter Name eines Interfaces f\u00fchrt unweigerlich dazu, dass hier viele Methoden landen werden, die m\u00f6glicherweise wenig gemein haben.<\/p>\n<p>Zu was f\u00fchrt denn ein umfangreiches Interface, welches Problem entsteht dabei? Bei einer Klasse streben wir lose Kopplung und eine hohe Koh\u00e4sion an. Lose Kopplung bedeutet hier, die Abh\u00e4ngigkeiten zwischen Klassen so gering wie m\u00f6glich zu halten, damit die Austauschbarkeit gegeben ist. Die Koh\u00e4sion gibt an, wie sehr eine Klasse tats\u00e4chlich zusammengeh\u00f6rig ist. Wenn nun alle Methoden der Klasse alle Klassenvariablen verwenden, so ist die Koh\u00e4sion hoch. Verwendet ein Teil der Methoden einen Teil der Klassenvariablen und alle anderen Methoden genau die anderen Klassenvariablen, so ist dies ein Indikator (Code-Smell) daf\u00fcr, dass die Klasse sich sinnvoll auf zwei Klassen aufteilen l\u00e4sst &#8211; die beiden Teile haben offensichtlich wenig miteinander zu tun.<\/p>\n<p>Ein Code-Smell f\u00fcr zu gro\u00dfe Interfaces findet ihr auf der Seite des Clients, also bei der Verwendung des Interfaces. Werden regelm\u00e4\u00dfig nur wenige bestimmte Methoden eines Interfaces verwendet oder in bestimmten Situationen nur eine kleine Auswahl von Methoden ben\u00f6tigt, so zeigt dies an, dass das Interface m\u00f6glicherweise zu umfangreich gew\u00e4hlt ist. Habt ihr schon einmal durch die lange Liste von Methoden eines Interfaces gebl\u00e4ttert um die richtige Methoden zu finden, die gerade aufzurufen ist? L\u00e4stig.<\/p>\n<p>Schauen wir uns ein kleines Beispiel aus der OSGI-Implementierung mit Felix an.<\/p>\n<pre class=\"lang:java decode:true \">package tutorial.example1;\r\n\r\nimport org.osgi.framework.BundleActivator;\r\nimport org.osgi.framework.BundleContext;\r\nimport org.osgi.framework.ServiceEvent;\r\nimport org.osgi.framework.ServiceListener;\r\n\r\npublic class Activator implements BundleActivator, ServiceListener {\r\n\r\n\t@Override\r\n\tpublic void serviceChanged(ServiceEvent event) {\r\n\t\t[...]\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void start(BundleContext context) throws Exception {\r\n        System.out.println(\"Starting to listen for service events.\");\r\n        context.addServiceListener(this);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void stop(BundleContext context) throws Exception {\r\n\t\tcontext.removeServiceListener(this);\r\n        System.out.println(\"Stopped listening for service events.\");\t\t\r\n\t}\r\n}\r\n<\/pre>\n<p>F\u00fcr das Interface <em>BundleActivator<\/em> werden die beiden Methoden <em>start(&#8230;)<\/em> und <em>stop(&#8230;)<\/em> implementiert, f\u00fcr das Interface <em>ServiceListener<\/em> die Methode <em>serviceChanged(&#8230;)<\/em>. Nun h\u00e4tten beide Methoden auch in einem einzigen Interface bereitgestellt werden k\u00f6nnen, jedoch w\u00e4re ich dann gezwungen gewesen, alle drei Methoden in einer Klasse zu implementieren. So habe ich die Wahl, eine separate <em>ServiceListener<\/em>-Implementierung bereitzustellen. Der <em>BundleActivator<\/em> f\u00fcgt den <em>ServiceListener<\/em> dem <em>BundleContext<\/em> hinzu bzw. entfernt diesen, das hat nichts mit der Reaktion auf das <em>serviceChanged<\/em>-Event zu tun.<\/p>\n<p>&nbsp;<\/p>\n<p>Ist es ein Problem, bei Bedarf zwei oder mehr Interfaces zu implementieren? Nat\u00fcrlich nicht. Dabei kann aber jeder Implementierer entscheiden, welche der Methoden ben\u00f6tigt und demnach welche Interfaces implementiert werden. Bei nur einem umfangreichen Interface besteht diese Wahlm\u00f6glichkeit erst gar nicht.<\/p>\n<p>Hei\u00dft dies nun, dass in jedem Interface nur eine Methode enthalten sein sollte? Wer gerade seine Schwarz-Wei\u00df-Brille aufgesetzt hat, mag dies so sehen, jedoch ist das nicht die Intention des ISP. Stattdessen sollten alle Methoden in einem Interface zusammengefasst werden, die tats\u00e4chlich auch zusammen geh\u00f6ren und eine bestimmte Anforderung abbilden &#8211; so klein wie m\u00f6glich, so gro\u00df wie n\u00f6tig. Diese Idee kommt euch nat\u00fcrlich aus dem <a href=\"http:\/\/invidit.de\/blog\/aus-prinzip-nur-eine-verantwortlichkeit\/\">Single Responsibility Principle<\/a> bekannt vor, oder?<\/p>\n<p>&nbsp;<\/p>\n<h2>Unsere Erfahrung mit dem ISP<\/h2>\n<p>Bei der Durchsicht unserer Codebasis sind mir tats\u00e4chlich sehr wenige Interfaces aufgefallen, die wir aufteilen sollten. Mit kleinen Interfaces zu arbeiten ist aus unserer Sicht deutlich einfacher und macht damit auch viel mehr Spa\u00df. Wie oben beschrieben wird die Definition der Interfaces durch einen aussagekr\u00e4ftigen Namen deutlich unterst\u00fctzt.<\/p>\n<p>Aber wie gehe ich vor, wenn ich denn nun ein Interface aufteilen m\u00f6chte, ohne s\u00e4mtliche bereits vorhandenen Implementierer zu \u00e4ndern? Das Refaktorisierungs-Muster &#8222;Extract Interface&#8220; kann auch in diesem Fall angewendet werden.<\/p>\n<ol>\n<li>Dazu wird ein neues Interface mit gut gew\u00e4hltem Namen angelegt, welches einen Teil der Methoden aus dem umfangreichen Interface aufnehmen soll.<\/li>\n<li>Im zweiten Schritt erweitert das bisherige Interface das gerade neu angelegte Interface.<\/li>\n<li>Anschlie\u00dfend k\u00f6nnen die zu extrahierenden Methoden in das neue Interface verschoben werden, ohne dass eine bestehende Implementierung zu \u00e4ndern ist.<\/li>\n<li>Nun kann das neue kleine Interface implementiert werden, ohne die zus\u00e4tzlichen Methoden aus dem alten Interface ber\u00fccksichtigen zu m\u00fcssen.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Auch das <em><strong>I<\/strong>nterface <strong>S<\/strong>egregation <strong>P<\/strong>rinciple<\/em> sorgt f\u00fcr eine modularisierte Anwendung mit kleinen, \u00fcberschaubaren und wiederverwendbaren Einheiten. Eine gute Namensgebung sowie Paket- \/ Namensraum-Organisation sorgt daf\u00fcr, dass jeder Entwickler genau die Klassen und Interfaces findet, die er ben\u00f6tigt.<\/p>\n<p>&nbsp;<\/p>\n<p>Viel Spa\u00df beim Anwenden.<\/p>\n<p>Eure Spa\u00df-Coder.<br \/>\nDieser Artikel basiert neben unseren Erfahrungen auf verschiedenen Internetquellen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. Wer von euch hat schon einmal ein Interface implementiert und findet anschlie\u00dfend ein oder mehrere leere Methoden oder Methoden mit throw new NotImplementedException() in seiner neuen Klasse wieder? Warum? Weil einfach zu viele Methoden im Interface vorgesehen sind, die mit der eigentlichen Aufgabe gar nichts zu tun haben. Hier werden wir immer wieder [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":344,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[48,57,20,44],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/334"}],"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=334"}],"version-history":[{"count":7,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/334\/revisions"}],"predecessor-version":[{"id":347,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/334\/revisions\/347"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/344"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=334"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=334"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=334"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}