Anleitung zum Glücklichsein

Hallo Spaß-Coder.

In unserem Artikel zum Thema Refactoring haben erläutert, was Refactoring ist und warum es jeder Entwickler machen sollte. Schön, 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: „Wie fange ich jetzt am Besten an?“? Gibt es da keine Anleitung zu? Nicht die eine große, aber viele kleine und diese wollen wir in diesem und weiteren Artikeln vorstellen.

 

Eine IDEe zur Selbsthilfe

Die integrierte Entwicklungsumgebung (IDE) Eclipse bietet für einige wenige Refaktorisierungen Unterstützung und führt diese nach Auswahl über das Kontextmenü automatisch durch. Sehr praktisch. Doch ist mir wohler dabei, wenn ich genau weiß, was die IDE da so macht bzw. machen sollte. Das ermöglicht mir, das Ergebnis zu prüfen bzw. im Vorfeld abzuwägen, ob genau mein gewünschtes Ergebnis am Ende herauskommt. Beginnen wir mit der wohl einfachsten und gleichzeitig wirkungsvollsten Refaktorisierung: Umbenennen (Rename). Welcher Entwickler freut sich nicht über Klassen, Methoden und Variablen mit sprechenden Namen? Wenn ich direkt am Namen erkennen kann, was hier passiert ohne lange den Code studieren zu müssen, ist das auch aus wirtschaftlichen Gründen gut – nämlich deutlich schneller. Im folgenden Beispiel eine Methode mit nicht allzu sprechendem Namen.

Beim Blick in die Methode wird vermutlich schnell klar werden, was der bessere Name der Methode ist.

Der Code ist noch übersichtlich genug, um schnell zu erfassen, was eigentlich geschieht. Bei längeren Methoden oder gleichzeitig nicht sprechenden Variablennamen wird es schon schwieriger. Daher kann in Eclipse über das Kontextmenü -> Refactor -> Rename die Methode umbenannt werden. Der Vorteil gegenüber dem einfachen Überschreiben des Methodennamens ist, dass Eclipse alle Vorkommen der Methode – also auch alle Aufrufe – ebenfalls direkt anpasst und durch den neuen Namen ersetzt. Das Tastenkürzel dafür lautet in der Standardeinstellung Alt+Shift+R. Wichtig: Eclipse berücksichtigt bei der Umbenennen üblicherweise keine Vorkommen in Textdateien, wie z.B. XML-Dateien. Daher ist in diesem Fall immer noch einmal manuell zu prüfen, ob alle Referenzen korrekt ersetzt sind.

Im Folgenden stellen wir das erste Refactoring-Pattern – also Anleitung – anhand eines Beispiels vor. Dabei zeigen wir auch die Unterstützung durch die IDE Eclipse.

 

Extract Interface

Die Ausgangssituation zeigt einen Auszug aus der Klasse Player eines TicTacToe-Spiels. Für den nächsten Zug wird die Eingabe auf der Konsole abgefragt. Die Implementierung dazu ist bereits in eine separate Klasse HumanStrategy ausgelagert.

Es besteht eine direkte Abhängigkeit von der Klasse Player zur Klasse HumanStrategy. Möchten wir nun die Methode getNextMove(…) in der Klasse Player testen, so interessiert uns nicht, was genau die Klasse HumanStrategy macht – sonst würden wir ja diese Klasse testen. Daher wäre es super, wenn wir für den Test keine echte Strategie sondern einen Dummy (Fake) verwenden könnten. Dies kann leicht über ein Mocking-Framework erreicht werden. Wollen wir dies „zu Fuß“ machen und damit auch unseren Produktionscode entkoppeln sowie für weitere Strategien (z.B. KI) vorbereiten, kann das Muster Extract Interface angewendet werden.

 

Anleitung

Die kurze Anleitung dazu besteht aus wenigen Schritten:

  1. Neue Schnittstelle erstellen (minimale Änderung)
  2. Produktions-Implementierung erstellen
  3. Fake-Implementierung erstellen
  4. Einfachen Test mit Übergabe der Fake-Implementierung
  5. Methode ändern, um neuen Parameter zu verwenden
  6. Methode mit Fake-Implementierung testen

1.) Als erstes legen wir ein Interface mit der gewünschten Signatur an. Das Schöne daran ist, damit kann noch nichts kaputt gehen 🙂

2.) Unsere Klasse HumanStrategy implementiert dann die neue Schnittstelle, wodurch sich sonst noch nichts ändert, die entsprechende Methode ist schließlich bereits vorhanden.

3.) Für unseren Test wird nun der Fake erstellt, welcher ebenfalls das neue Interface MoveStrategy implementiert. (Wenn ihr gerade nicht mehr so genau wisst, was ein Fake ist, lest es nochmal nach.)

4.) Damit können wir jetzt einen Test erstellen, der – wie so üblich bei test-driven Unit Tests – erst einmal fehlt schlägt.

5.) Dazu passen wir den Konstruktor der Klasse Player an. Die zu testende Methode arbeitet unverändert mit der Klassenvariable strategy, hinter der sich nun das zuvor erstellt Interface verbirgt.

6.) Nun können wir unseren Test ausführen und erhalten die Bestätigung, dass die Methode genau wie erwartet funktioniert.

 

In der IDE

Was macht nun Eclipse, wenn ich das Refactoring durch die IDE ausführen lasse (statt es manuell zu machen)? In der Klasse HumanStrategy wähle ich per Kontextmenü Refactor -> Extract Interface aus.

ExtractInterfaceEclipseDialog

Ich kann im Dialog den Namen des neuen Interfaces angeben und neben wenigen Optionen die Methoden auswählen, welche das Interface von der Klasse übernehmen soll. In der Vorschau werden mit alle zu ändernden Klasse angezeigt und bei Bestätigung des Dialogs werden genau dieselben Änderungen durchgeführt, wie wir sie vorher manuell ausgeführt haben:

  • die Klasse HumanStrategy implementiert das Interface MoveStrategyIDE
  • die Klasse Player erwartet über den Konstruktor jetzt eine Implementierung von MoveStrategyIDE
  • die Klassenvariable ist ebenfalls vom Typ MoveStrategyIDE

Leider nimmt uns Eclipse dabei nicht das Schreiben des Unit Tests ab… schade eigentlich 😉

Mir ist derzeit kein Anwendungsfall bekannt, in dem Eclipse das Muster Extract Interface nicht sauber ausführt. Eine manuelle Überprüfung der Änderung – auch über die Vorschau im Dialog – halte ich jedoch in jedem Fall für ratsam.

 

Unsere Erfahrung mit Extract Interface

Insbesondere die grundsätzlichen kleinen Schritte, die bereits jemand definiert hat, um das Muster auszuführen, helfen uns dabei den Fokus auf die aktuelle Änderung zu behalten. Andernfalls gab es bei uns durchaus Coding-Sessions in denen wir nach einigen Stunden Refactoring alle Änderungen verworfen haben. Warum? Weil wir zu viele Änderungen auf einmal durchgeführt haben und den Code dann nicht mehr zum laufen bringen konnten. Gemäß 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äufiger vor, dass das Muster Extract Interface hilft, nachträglich einen Test zu ergänzen oder Funktionalität zu erweitern.

 

Zusammenfassung

Insbesondere beim Einfügen von Unit Tests besteht die Notwendigkeit, Abhängigkeiten zwischen der zu testenden Klasse und weiteren Klassen aufzuheben, um nur genau eine Funktionalität zu testen. Weiterhin kann durch das Einfügen eines Interfaces die Kopplung zwischen Klassen gelöst werden. Dadurch entsteht eine stärkere Komponentenorientierung und Austauschbarkeit. Auch wenn die IDE uns hier die Arbeit etwas abnimmt, sollte ich doch wissen, was genau geändert werden muss um das Ergebnis zu prüfen und zu verstehen.

 

Wir wünschen euch viel Spaß beim Ausprobieren.

Eure Spaß-Coder

 

Dieser Artikel basiert neben unseren Erfahrungen auf folgenden Quellen:

  • https://sourcemaking.com/refactoring
  • Martin, Robert C. Clean Code-Refactoring, Patterns, Testen und Techniken für sauberen Code: Deutsche Ausgabe. MITP-Verlags GmbH & Co. KG, 2013.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.