{"id":628,"date":"2015-08-26T16:00:50","date_gmt":"2015-08-26T14:00:50","guid":{"rendered":"http:\/\/invidit.de\/blog\/?p=628"},"modified":"2015-09-20T10:24:58","modified_gmt":"2015-09-20T08:24:58","slug":"ausbruchsversuch-gelungen","status":"publish","type":"post","link":"https:\/\/invidit.de\/blog\/ausbruchsversuch-gelungen\/","title":{"rendered":"Ausbruchsversuch gelungen"},"content":{"rendered":"<p>Hallo Spa\u00df-Coder.<\/p>\n<p>In diesem Artikel besch\u00e4ftigen wir uns wieder mit dem Thema Refactoring und stellen zwei weitere Refaktorisierungsmuster vor.<\/p>\n<p>Ihr habt eine Klasse, in der ihr eine bestimmte Methode \u00e4ndern m\u00fcsst. Habt ihr schon einmal erlebt, dass die Klasse f\u00fcr einen Test nicht instanziiert werden kann? Zu viele Abh\u00e4ngigkeiten an Datenbanken, Dateisystem oder anderen komplexen Klassen verhindern leider die Klasse per Konstruktor zu erzeugen. Was nun? Die Methode ohne Unit-Tests zu \u00e4ndern ist zu riskant. Aus diesem Grund m\u00fcssen erst einmal Tests geschrieben werden &#8211; aber wie?<\/p>\n<p>Vielleicht ist es in solchen F\u00e4llen m\u00f6glich mit Hilfe eines Mocking-Frameworks die gew\u00fcnschte Methode testbar zu machen und alle Abh\u00e4ngigkeiten zu &#8222;entfernen&#8220;. Sollte dies nicht m\u00f6glich sein (oder wilden Spaghetti-Mocking-Code ergeben), helfen vielleicht diese beiden Muster.<\/p>\n<p>&nbsp;<\/p>\n<h1>Expose Static Method<\/h1>\n<p>Manchmal m\u00fcssen wir Klassen ver\u00e4ndern, die wir nicht testen k\u00f6nnen, weil die Methode, die wir \u00e4ndern m\u00f6chten leider privat ist. Wir \u00e4ndern aber keinen Code, der nicht getestet ist, weil die Gefahr zu hoch ist, dass wir etwas kaputtmachen.<\/p>\n<p>Verwendet die betroffene Methode keine Instanzvariablen oder andere Methoden der Klasse, k\u00f6nnen wir diese einfach in eine \u00f6ffentliche, statische Methode umwandeln. Diese statische Methode l\u00e4sst sich nun problemlos testen.<\/p>\n<p>In aller Regel f\u00fchren Methoden dieser Art dazu, dass die Klasse eine geringe Koh\u00e4sion aufweist. Wenn ihr gerade nicht mehr genau wisst, was das nochmal gleich bedeutet und warum das nicht\u00a0 gut ist, lest nochmal die Artikel <a href=\"http:\/\/invidit.de\/blog\/teile-und-herrsche\/\" target=\"_blank\">Teile und Herrsche<\/a> sowie <a href=\"http:\/\/invidit.de\/blog\/aus-prinzip-nur-eine-verantwortlichkeit\/\" target=\"_blank\">Aus Prinzip nur eine Verantwortlichkeit<\/a>. Darin sind wir auf das Thema bereits eingegangen.<\/p>\n<p>Wir wollen bei der Refaktorisierung m\u00f6glichst kleine, risikoarme Schritte machen. Deshalb ist es besser nicht gleich die Koh\u00e4sion g\u00e4nzlich aufzul\u00f6sen, sondern zun\u00e4chst den betroffenen Code durch Tests abzusichern und anschlie\u00dfend erst zu ver\u00e4ndern.<\/p>\n<p>&nbsp;<\/p>\n<h2>Anleitung<\/h2>\n<p>Wie aber gehen wir nun vor, um unsere Methode statisch und damit testbar zu machen?<\/p>\n<p>Die kurze Anleitung dazu besteht aus wenigen Schritten:<\/p>\n<ol>\n<li>Unit-Test f\u00fcr die neue, statische Methode schreiben<\/li>\n<li>Methodenrumpf der Methode in eine neue, statische Methode extrahieren<\/li>\n<li>Code kompilieren (lean on the compiler)<\/li>\n<li>Nicht-statische Verwendungen innerhalb der neuen Methode ebenfalls statisch machen<\/li>\n<\/ol>\n<p>Haben\u00a0 wir alle Schritte ausgef\u00fchrt, sollte unser Code kompilierbar sein und der Unit-Test aus Schritt 1. <span style=\"color: #339966;\">gr\u00fcn<\/span> sein.<\/p>\n<p>&nbsp;<\/p>\n<h2>Hinweise<\/h2>\n<p>Manchmal m\u00f6chten wir die Methode aber gar nicht nach au\u00dfen sichtbar machen. Wir m\u00f6chten unsere Klassen schlie\u00dflich absichern. Insbesondere, wenn wir in Schritt 4. weitere nicht-statische Abh\u00e4ngigkeiten aufl\u00f6sen.<\/p>\n<p>In einem solchen Fall bieten uns Sprachen wir Java oder C# die M\u00f6glichkeit, die Methode auf den aktuellen Bereich zu beschr\u00e4nken. In Java k\u00f6nnen wir die Klasse <em>package scoped <\/em>machen; in C# nutzen wir das Schl\u00fcsselwort <em>internal<\/em>. Beides f\u00fchrt dazu, dass wir in unserem Test, welcher im gleichen package liegt oder Zugriff auf internals hat, auf die Methode zugreifen k\u00f6nnen.<\/p>\n<p>Eine weitere M\u00f6glichkeit liegt darin, die Methode <em>protected <\/em>zu definieren und die Test-Klasse davon abzuleiten. In diesem Fall k\u00f6nnen wir auf die Methode zugreifen, nicht abgeleitete Klassen aber nicht. (Dies funktioniert dann auch in Sprachen wie C++).<\/p>\n<p>&nbsp;<\/p>\n<h1>Break Out Method Object<\/h1>\n<p>Was aber tun wir, wenn unsere zu testende Methode sehr lang ist und viele Abh\u00e4ngigkeiten hat? Einen Test zu schreiben w\u00fcrde bedeuten, die gesamte, riesige Klasse zu instanziieren und alle Abh\u00e4ngigkeiten in unserem Test aufzubauen. Das Muster <em>Expose Static Method <\/em>ist hier mitunter ungeeignet, wenn es einfach zu viele Abh\u00e4ngigkeiten gibt, die Koh\u00e4sion der Methode also recht hoch ist.<\/p>\n<p>In einem solchen Fall hilft es, der Methode eine eigene Klasse zu spendieren. Wenn sie so gro\u00df und umfangreich ist, tut sie oft ohnehin viel zu viel und geh\u00f6rt h\u00e4ufig gar nicht in die Klasse, in der wir sie vorgefunden haben.<\/p>\n<p>Beim Muster <em>Break Out Method Object <\/em>ziehen wir die zu testende Methode mitsamt allen Abh\u00e4ngigkeiten in eine eigene Klasse. Dabei werden nur diejenigen Abh\u00e4ngigkeiten mitgenommen, die von der zu testenden Methode verwendet werden. Alle anderen bleiben in der bisherigen Klasse. Dies sorgt f\u00fcr eine bessere Trennung der Aufgaben, verringert die Abh\u00e4ngigkeiten und erleichtert es uns, die Methode in der neuen Klasse zu testen.<\/p>\n<p>&nbsp;<\/p>\n<h2>Anleitung<\/h2>\n<p>Das Erstellen einer neuen Klasse und das \u00fcberf\u00fchren der Methode dort hinein ist ein wenig aufwendiger als im obigen Muster.<\/p>\n<p>Hier die Schritt-f\u00fcr-Schritt Anleitung dazu:<\/p>\n<ol>\n<li>Neue, leere Klasse f\u00fcr die Methode erstellen<\/li>\n<li>Konstruktor mit der Signatur der bisherigen Methode erstellen\n<ul>\n<li>ggf. muss eine Instanz der bisherigen Klasse \u00fcbergeben werden (bei Zugriffen auf Instanzvariablen; erstes Argument des Konstruktors)<\/li>\n<\/ul>\n<\/li>\n<li>F\u00fcr alle Konstruktor-Parameter Instanzvariablen erstellen und im Konstruktor zuweisen<\/li>\n<li>Leere Methode ohne Parameter erstellen (h\u00e4ufig <em>run()<\/em>)<\/li>\n<li>Methodenrumpf aus der alten Klasse in die leere Methode kopieren und kompilieren (lean on the compiler)<\/li>\n<li>Kompilierfehler korrigieren, ggf. Zugriff auf alte Klasse durch public Methoden oder via Getter erm\u00f6glichen<\/li>\n<li>Alte Methode so \u00e4ndern, dass die neue Klasse instanziiert und dann verwendet wird<\/li>\n<li>Ggf. Extract Interface nutzen, um die Abh\u00e4ngigkeit von der urspr\u00fcnglichen Klasse aufzuheben<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<h2>Hinweise<\/h2>\n<p>Wenn wir eine Methode, die vielleicht sogar privat ist, in eine neue Klasse \u00fcberf\u00fchren, f\u00fchrt das dazu, dass wir die Sichtbarkeit der Methode erh\u00f6hen. Dar\u00fcber hinaus haben wir nun eine Klasse, die an genau einer Stelle instanziiert wird, n\u00e4mlich da, wo sie bisher stand. Das k\u00f6nnte zu einem unguten Gef\u00fchl f\u00fchren.<\/p>\n<p>Die Vorteile sind hier vielleicht nicht direkt ersichtlich. Neben dem offensichtlichen Punkt, dass wir die Methode nun mit viel weniger Aufwand testen k\u00f6nnen, haben wir eine gute Basis geschaffen, das Design der Klassen weiter zu verbessern. Haben wir in Punkt 8 ein Interface herausgezogen, k\u00f6nnen wir nun den n\u00e4chsten Schritt gehen und die Abh\u00e4ngigkeit aufl\u00f6sen, z. B. in dem wir das\u00a0Dependency Inversion Principle anwenden.<\/p>\n<p>Durch die Aufteilung mithilfe des <em>Break Out Method Object<\/em> laden wir andere Entwickler (und auch uns selbst) dazu ein, das Design weiter zu verbessern. Wir k\u00f6nnen z.B. die lange Methode mithilfe von <a href=\"http:\/\/invidit.de\/blog\/anleitung-zum-gluecklichsein\/\" target=\"_blank\">Extract Method<\/a> weiter aufteilen und so den Code besser lesbar machen.<\/p>\n<p>&nbsp;<\/p>\n<h1>Zusammenfassung<\/h1>\n<p>Gewachsener Code, der nicht durch Tests abgesichert ist, ist wie ein Minenfeld. Man wei\u00df nie so genau, was beim n\u00e4chsten Schritt passiert.<\/p>\n<p>Durch die beiden hier aufgef\u00fchrten Refactoring Patterns bekommen wir die M\u00f6glichkeit, ohne gro\u00dfes Risiko, interne Methoden testbar zu machen. Dann k\u00f6nnen wir gefahrlos das Design der Klassen weiter verbessern oder unsere \u00c4nderung an der Klassen vornehmen.<\/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 id=\"gs_cit0\" class=\"gs_citr\" tabindex=\"0\">Feathers, Michael C. <i><a href=\"https:\/\/books.google.de\/books?id=EO_ywJYer6kC&amp;printsec=frontcover\" target=\"_blank\">Effektives Arbeiten mit Legacy Code: Refactoring und Testen bestehender Software<\/a><\/i>. mitp Verlags GmbH &amp; Co. KG, 2011.<\/li>\n<li class=\"gs_citr\" tabindex=\"0\">sourcemaking.com, <em><a href=\"https:\/\/sourcemaking.com\/refactoring\/replace-method-with-method-object\" target=\"_blank\">Replace Method with Method Object<\/a><\/em><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Hallo Spa\u00df-Coder. In diesem Artikel besch\u00e4ftigen wir uns wieder mit dem Thema Refactoring und stellen zwei weitere Refaktorisierungsmuster vor. Ihr habt eine Klasse, in der ihr eine bestimmte Methode \u00e4ndern m\u00fcsst. Habt ihr schon einmal erlebt, dass die Klasse f\u00fcr einen Test nicht instanziiert werden kann? Zu viele Abh\u00e4ngigkeiten an Datenbanken, Dateisystem oder anderen komplexen [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":638,"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\/628"}],"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=628"}],"version-history":[{"count":9,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/628\/revisions"}],"predecessor-version":[{"id":640,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/posts\/628\/revisions\/640"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media\/638"}],"wp:attachment":[{"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/media?parent=628"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/categories?post=628"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/invidit.de\/blog\/wp-json\/wp\/v2\/tags?post=628"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}