{"id":601,"date":"2015-08-19T16:14:44","date_gmt":"2015-08-19T14:14:44","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=601"},"modified":"2015-09-20T10:26:55","modified_gmt":"2015-09-20T08:26:55","slug":"anleitung-zum-gluecklichsein","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/anleitung-zum-gluecklichsein\/","title":{"rendered":"Anleitung zum Gl\u00fccklichsein"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>In unserem Artikel zum Thema <a href=\"http:\/\/invidit.de\/blog\/refactoring-was-es-ist-und-warum-jeder-entwickler-es-machen-sollte\/\">Refactoring<\/a> haben erl\u00e4utert, was Refactoring ist und warum es jeder Entwickler machen sollte. Sch\u00f6n, aber wie mache ich das denn jetzt genau? Wie gehe ich dabei vor? Habt ihr auch schon einmal den Versuch gestartet, euren Code refaktorisieren zu wollen und dann vor dem Code gesessen mit der Frage: &#8222;Wie fange ich jetzt am Besten an?&#8220;? Gibt es da keine Anleitung zu? Nicht die eine gro\u00dfe, aber viele kleine und diese wollen wir in diesem und weiteren Artikeln vorstellen.<\/p>\n<p>&nbsp;<\/p>\n<h1>Eine IDEe zur Selbsthilfe<\/h1>\n<p>Die integrierte Entwicklungsumgebung (IDE) Eclipse bietet f\u00fcr einige wenige Refaktorisierungen Unterst\u00fctzung und f\u00fchrt diese nach Auswahl \u00fcber das Kontextmen\u00fc automatisch durch. Sehr praktisch. Doch ist mir wohler dabei, wenn ich genau wei\u00df, was die IDE da so macht bzw. machen sollte. Das erm\u00f6glicht mir, das Ergebnis zu pr\u00fcfen bzw. im Vorfeld abzuw\u00e4gen, ob genau mein gew\u00fcnschtes Ergebnis am Ende herauskommt. Beginnen wir mit der wohl einfachsten und gleichzeitig wirkungsvollsten Refaktorisierung: Umbenennen (Rename). Welcher Entwickler freut sich nicht \u00fcber Klassen, Methoden und Variablen mit <a href=\"http:\/\/invidit.de\/blog\/basics-namen\/\">sprechenden Namen<\/a>? Wenn ich direkt am Namen erkennen kann, was hier passiert ohne lange den Code studieren zu m\u00fcssen, ist das auch aus wirtschaftlichen Gr\u00fcnden gut &#8211; n\u00e4mlich deutlich schneller. Im folgenden Beispiel eine Methode mit nicht allzu sprechendem Namen.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Beispiel Methode ohne sprechenden Namen\">public Player things() {\r\n\tPlayer player = new Player();\r\n\t\r\n\tplayer.name = this.name;\r\n\tplayer.symbol = this.symbol;\r\n\tplayer.strategy = this.strategy;\r\n\t\r\n\treturn player;\r\n}<\/pre>\n<p>Beim Blick in die Methode wird vermutlich schnell klar werden, was der bessere Name der Methode ist.<\/p>\n<pre class=\"lang:java decode:true\" title=\"Besserer Methodenname\">public Player clone() {\r\n\tPlayer player = new Player();\r\n\t\r\n\tplayer.name = this.name;\r\n\tplayer.symbol = this.symbol;\r\n\tplayer.strategy = this.strategy;\r\n\t\r\n\treturn player;\r\n}<\/pre>\n<p>Der Code ist noch \u00fcbersichtlich genug, um schnell zu erfassen, was eigentlich geschieht. Bei l\u00e4ngeren Methoden oder gleichzeitig nicht sprechenden Variablennamen wird es schon schwieriger. Daher kann in Eclipse \u00fcber das Kontextmen\u00fc -&gt; Refactor -&gt; Rename die Methode umbenannt werden. Der Vorteil gegen\u00fcber dem einfachen \u00dcberschreiben des Methodennamens ist, dass Eclipse alle Vorkommen der Methode &#8211; also auch alle Aufrufe &#8211; ebenfalls direkt anpasst und durch den neuen Namen ersetzt. Das Tastenk\u00fcrzel daf\u00fcr lautet in der Standardeinstellung <em>Alt+Shift+R<\/em>. <strong>Wichtig<\/strong>: Eclipse ber\u00fccksichtigt bei der Umbenennen \u00fcblicherweise keine Vorkommen in Textdateien, wie z.B. XML-Dateien. Daher ist in diesem Fall immer noch einmal manuell zu pr\u00fcfen, ob alle Referenzen korrekt ersetzt sind.<\/p>\n<p>Im Folgenden stellen wir das erste Refactoring-Pattern &#8211; also Anleitung &#8211; anhand eines Beispiels vor. Dabei zeigen wir auch die Unterst\u00fctzung durch die IDE Eclipse.<\/p>\n<p>&nbsp;<\/p>\n<h1>Extract Interface<\/h1>\n<p>Die Ausgangssituation zeigt einen Auszug aus der Klasse <em>Player<\/em> eines TicTacToe-Spiels. F\u00fcr den n\u00e4chsten Zug wird die Eingabe auf der Konsole abgefragt. Die Implementierung dazu ist bereits in eine separate Klasse <em>HumanStrategy<\/em> ausgelagert.<\/p>\n<pre class=\"lang:java decode:true\" title=\"Ausgangssituation Klasse Spieler\">public class Player {\r\n\r\n\tprivate String name;\r\n\tprivate HumanStrategy strategy;\r\n\tprivate String symbol;\r\n\t\r\n\tpublic Player(String name, String symbol, HumanStrategy strategy) {\r\n\t\tthis.name = name;\r\n\t\tthis.symbol = symbol;\r\n\t\tthis.strategy = strategy;\r\n\t}\r\n\t\r\n\t[...]\t\r\n\r\n\tpublic int getNextMove(GameState state) {\r\n\t\treturn this.strategy.getNextPosition(state);\r\n\t}\r\n\t\r\n\t[...]\t\r\n}<\/pre>\n<p>Es besteht eine direkte Abh\u00e4ngigkeit von der Klasse <em>Player<\/em> zur Klasse <em>HumanStrategy<\/em>. M\u00f6chten wir nun die Methode <em>getNextMove(&#8230;)<\/em> in der Klasse <em>Player<\/em> testen, so interessiert uns nicht, was genau die Klasse <em>HumanStrategy<\/em> macht &#8211; sonst w\u00fcrden wir ja diese Klasse testen. Daher w\u00e4re es super, wenn wir f\u00fcr den Test keine echte Strategie sondern einen Dummy (Fake) verwenden k\u00f6nnten. Dies kann leicht \u00fcber ein Mocking-Framework erreicht werden. Wollen wir dies &#8222;zu Fu\u00df&#8220; machen und damit auch unseren Produktionscode entkoppeln sowie f\u00fcr weitere Strategien (z.B. KI) vorbereiten, kann das Muster <strong>Extract Interface<\/strong> angewendet werden.<\/p>\n<p>&nbsp;<\/p>\n<h2>Anleitung<\/h2>\n<p>Die kurze Anleitung dazu besteht aus wenigen Schritten:<\/p>\n<ol>\n<li>Neue Schnittstelle erstellen (minimale \u00c4nderung)<\/li>\n<li>Produktions-Implementierung erstellen<\/li>\n<li>Fake-Implementierung erstellen<\/li>\n<li>Einfachen Test mit \u00dcbergabe der Fake-Implementierung<\/li>\n<li>Methode \u00e4ndern, um neuen Parameter zu verwenden<\/li>\n<li>Methode mit Fake-Implementierung testen<\/li>\n<\/ol>\n<p>1.) Als erstes legen wir ein Interface mit der gew\u00fcnschten Signatur an. Das Sch\u00f6ne daran ist, damit kann noch nichts kaputt gehen \ud83d\ude42<\/p>\n<pre class=\"lang:java decode:true\" title=\"Interace Strategy\">public interface MoveStrategy {\r\n   public int getNextPosition(GameState state);\r\n}<\/pre>\n<p>2.) Unsere Klasse <em>HumanStrategy<\/em> implementiert dann die neue Schnittstelle, wodurch sich sonst noch nichts \u00e4ndert, die entsprechende Methode ist schlie\u00dflich bereits vorhanden.<\/p>\n<pre class=\"lang:java decode:true\" title=\"Produktionsimplementer erstellen\">public class HumanStrategy implements MoveStrategy {\r\n\tpublic int getNextPosition(GameState state) {\r\n        [...]\r\n        }\r\n}\r\n<\/pre>\n<p>3.) F\u00fcr unseren Test wird nun der Fake erstellt, welcher ebenfalls das neue Interface <em>MoveStrategy<\/em> implementiert. (Wenn ihr gerade nicht mehr so genau wisst, was ein Fake ist, <a href=\"http:\/\/invidit.de\/blog\/von-tests-und-komponenten\/\">lest es nochmal nach<\/a>.)<\/p>\n<pre class=\"lang:java decode:true\" title=\"Fake-Implementierer\">public class FakeMoveStrategy implements MoveStrategy {\r\n\tpublic int getNextPosition(GameState state) {\r\n\t\treturn 0;\r\n\t}\r\n}<\/pre>\n<p>4.) Damit k\u00f6nnen wir jetzt einen Test erstellen, der &#8211; wie so \u00fcblich bei <a href=\"http:\/\/invidit.de\/blog\/vom-testen-getrieben\/\">test-driven Unit Tests<\/a> &#8211; erst einmal fehlt schl\u00e4gt.<\/p>\n<pre class=\"lang:default decode:true\" title=\"Neuer Unit Test\">@Test\r\npublic void testGetNextMoveReturnsValueFromStrategy() {\r\n\tPlayer player = new Player(\"Hugo\", \"X\", new FakeMoveStrategy());\r\n        assertThat(player.getNextMove(null)).isEqualTo(0);\r\n}<\/pre>\n<p>5.) Dazu passen wir den Konstruktor der Klasse <em>Player <\/em>an. Die zu testende Methode arbeitet unver\u00e4ndert mit der Klassenvariable <em>strategy<\/em>, hinter der sich nun das zuvor erstellt Interface verbirgt.<\/p>\n<pre class=\"lang:java decode:true \" title=\"Ge\u00e4nderte Klasse Spieler\">public class Player {\r\n\r\n\tprivate String name;\r\n\tprivate MoveStrategy strategy;\r\n\tprivate String symbol;\r\n\r\n\tpublic Player(String name, String symbol, MoveStrategy strategy) {\r\n\t\tthis.name = name;\r\n\t\tthis.symbol = symbol;\r\n\t\tthis.strategy = strategy;\r\n\t}\r\n        [...]\r\n}<\/pre>\n<p>6.) Nun k\u00f6nnen wir unseren Test ausf\u00fchren und erhalten die Best\u00e4tigung, dass die Methode genau wie erwartet funktioniert.<\/p>\n<p>&nbsp;<\/p>\n<h2>In der IDE<\/h2>\n<p>Was macht nun Eclipse, wenn ich das Refactoring durch die IDE ausf\u00fchren lasse (statt es manuell zu machen)? In der Klasse <em>HumanStrategy<\/em> w\u00e4hle ich per Kontextmen\u00fc <em>Refactor -&gt; Extract Interface<\/em> aus.<\/p>\n<p><a href=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/08\/ExtractInterfaceEclipseDialog.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-614\" src=\"http:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/08\/ExtractInterfaceEclipseDialog.png\" alt=\"ExtractInterfaceEclipseDialog\" width=\"494\" height=\"487\" srcset=\"https:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/08\/ExtractInterfaceEclipseDialog.png 494w, https:\/\/invidit.de\/blog\/wp-content\/uploads\/2015\/08\/ExtractInterfaceEclipseDialog-300x296.png 300w\" sizes=\"(max-width: 494px) 100vw, 494px\" \/><\/a><\/p>\n<p>Ich kann im Dialog den Namen des neuen Interfaces angeben und neben wenigen Optionen die Methoden ausw\u00e4hlen, welche das Interface von der Klasse \u00fcbernehmen soll. In der Vorschau werden mit alle zu \u00e4ndernden Klasse angezeigt und bei Best\u00e4tigung des Dialogs werden genau dieselben \u00c4nderungen durchgef\u00fchrt, wie wir sie vorher manuell ausgef\u00fchrt haben:<\/p>\n<ul>\n<li>die Klasse <em>HumanStrategy<\/em> implementiert das Interface <em>MoveStrategyIDE<\/em><\/li>\n<li>die Klasse <em>Player<\/em> erwartet \u00fcber den Konstruktor jetzt eine Implementierung von <em>MoveStrategyIDE<\/em><\/li>\n<li>die Klassenvariable ist ebenfalls vom Typ <em>MoveStrategyIDE<\/em><\/li>\n<\/ul>\n<p>Leider nimmt uns Eclipse dabei nicht das Schreiben des Unit Tests ab&#8230; schade eigentlich \ud83d\ude09<\/p>\n<p>Mir ist derzeit kein Anwendungsfall bekannt, in dem Eclipse das Muster <strong>Extract Interface<\/strong> nicht sauber ausf\u00fchrt. Eine manuelle \u00dcberpr\u00fcfung der \u00c4nderung &#8211; auch \u00fcber die Vorschau im Dialog &#8211; halte ich jedoch in jedem Fall f\u00fcr ratsam.<\/p>\n<p>&nbsp;<\/p>\n<h3>Unsere Erfahrung mit Extract Interface<\/h3>\n<p>Insbesondere die grunds\u00e4tzlichen kleinen Schritte, die bereits jemand definiert hat, um das Muster auszuf\u00fchren, helfen uns dabei den Fokus auf die aktuelle \u00c4nderung zu behalten. Andernfalls gab es bei uns durchaus Coding-Sessions in denen wir nach einigen Stunden Refactoring alle \u00c4nderungen verworfen haben. Warum? Weil wir zu viele \u00c4nderungen auf einmal durchgef\u00fchrt haben und den Code dann nicht mehr zum laufen bringen konnten. Gem\u00e4\u00df den SOLID-Prinzipien versuchen wir bereits im ersten Schritt dort ein Interface zu verwenden, wo ein Test dies erfordert oder eine Entkopplung sinnvoll ist. Bei Legacy-Code kommt es h\u00e4ufiger vor, dass das Muster <strong><em>Extract Interface<\/em><\/strong> hilft, nachtr\u00e4glich einen Test zu erg\u00e4nzen oder Funktionalit\u00e4t zu erweitern.<\/p>\n<p>&nbsp;<\/p>\n<h3>Zusammenfassung<\/h3>\n<p>Insbesondere beim Einf\u00fcgen von Unit Tests besteht die Notwendigkeit, Abh\u00e4ngigkeiten zwischen der zu testenden Klasse und weiteren Klassen aufzuheben, um nur genau eine Funktionalit\u00e4t zu testen. Weiterhin kann durch das Einf\u00fcgen eines Interfaces die Kopplung zwischen Klassen gel\u00f6st werden. Dadurch entsteht eine st\u00e4rkere Komponentenorientierung und Austauschbarkeit. Auch wenn die IDE uns hier die Arbeit etwas abnimmt, sollte ich doch wissen, was genau ge\u00e4ndert werden muss um das Ergebnis zu pr\u00fcfen und zu verstehen.<\/p>\n<p>&nbsp;<\/p>\n<p>Wir w\u00fcnschen euch viel Spa\u00df beim Ausprobieren.<\/p>\n<p>Eure Spa\u00df-Coder<\/p>\n<p>&nbsp;<\/p>\n<p>Dieser Artikel basiert neben unseren Erfahrungen auf folgenden Quellen:<\/p>\n<ul>\n<li><a href=\"https:\/\/sourcemaking.com\/refactoring\">https:\/\/sourcemaking.com\/refactoring<\/a><\/li>\n<li>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<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. In unserem Artikel zum Thema Refactoring haben erl\u00e4utert, was Refactoring ist und warum es jeder Entwickler machen sollte. Sch\u00f6n, aber wie mache ich das denn jetzt genau? Wie gehe ich dabei vor? Habt ihr auch schon einmal den Versuch gestartet, euren Code refaktorisieren zu wollen und dann vor dem Code gesessen mit der [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":619,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[90],"tags":[53],"_links":{"self":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/601"}],"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=601"}],"version-history":[{"count":20,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/601\/revisions"}],"predecessor-version":[{"id":623,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/601\/revisions\/623"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/619"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=601"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=601"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=601"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}