Title: Refactoring Java Code
1Refactoring Java Code
Arno.Haase_at_Haase-Consulting.com Arno.Haase_at_acm.org
www.Haase-Consulting.com
2Übersicht
- ? Einführung
- Das Umfeld für Refactoring
- Code Smells als Wegweiser
- Automatisierte Tests mit JUnit
- Konkrete Refactorings
- Beispiel
- Toolunterstützung
3Refactoring, Martin Fowler
- Dieses Tutorial beruht im Wesentlichen auf dem
Buch Refactoring Improving the Design of
Existing Code von Martin Fowler. - Standardwerk
- Abweichungen im Detail
4Refactoring Definition
Refactoring ist die Verbesserung der
Qualität von vorhandenem Quell- text ohne
Veränderung der Funktionalität.
5Problem
- Das Design eines Systems neigt dazu, im Laufe der
Zeit immer schlechter zu werden. - Neue Funktionalität wird gefordert, alte entfällt
- Vorhandene Anforderungen werden geändert
- Das Verständnis des Systems ändert sich
6Refactoring als Gegenmittel
- Um dem entgegenzuwirken, muss das Design mit dem
System wachsen. - Kleine Schritte reduzieren das Risiko
- Trennung zwischen Refactoring und Erweiterung des
Systems hilft zu fokussieren - Arbeit am System stößt auf sich ändernde Teile
7Demonstration
- Ein Quelltext sagt mehr als tausend Folien...
- Der Quelltext ist im Internet verfügbar
www.haase-consulting.com/download/oop2002
8Was ist geschehen?
- Ein Stück Software wurde überarbeitet, ohne dass
sich sein Verhalten geändert hat. - Auslöser eine anstehende Änderung
- Code war unnötig kompliziert
- Änderungen in kleinen Schritten
9Wann Refactoring
- Der richtige Zeitpunkt für Refactoring ist, wenn
man sich ohnehin mit dem Quelltext beschäftigt. - Beim Debugging
- Beim Erweitern/Ändern
- Wenn ein Kollege mit einer Frage kommt
10Kleine Schritte
- Refactoring funktioniert am besten, wenn man es
in kleinen Schritten tut. - Schutz vor Flüchtigkeitsfehlern
- Man behält den Überblick
- Bei Problem einfach einen Schritt zurück
11Die beiden Hüte
- Refactoring und Erweiterung des Systems wechseln
sich ab. - Saubere Trennung für besseren Überblick
- Zettel und Stift als Gedächtnisstütze
- Man hat entweder den Refactoring-Hut oder den
Erweiterungs-Hut auf
12Zusammenfassung
- Refactoring erlaubt es, nachträglich
Designentscheidungen zu ändern. - Zeitpunkt wenn man ohnehin mit dem Code zu tun
hat - Kleine Schritte Entspannt bleiben, bei Problem
einen Schritt zurück.
13Übersicht
- Einführung
- ? Das Umfeld für Refactoring
- Code Smells als Wegweiser
- Automatisierte Tests mit JUnit
- Konkrete Refactorings
- Beispiel
- Toolunterstützung
14Altersschwäche
- Software kann an Altersschwäche sterben.
- Design wächst nicht mit den Anforderungen
- Wartung als Albtraum
- Manchmal schon vor Inbetriebnahme...
15Hellseherei
- Fixiertes Design vorab ist problematisch.
- Anforderungen ändern sich
- Das Verständnis wächst
- Hellseherei funktioniert oft nicht
- Ausnahme feste Anforderungen, erfahrenes Team
(z.B. reine Migration)
16Traditionelles Vorgehen
- Das Wasserfall-Modell ist immer noch sehr
verbreitet. - Änderungen werden mit der Zeit teurer
- Risiko minimieren
17Änderungen billig machen
- Agile Vorgehensmodelle unterstützen späte
Änderungen - Z.B. eXtreme Programming
- Weniger Gewicht auf Planung am Anfang
- System profitiert von wachsender Erfahrung
18Kriterien für das Vorgehensmodell
- Refactoring funktioniert am besten in einem
Vorgehensmodell, bei dem Änderungen - billig und
- sicher sind.
- Dann kann das System von der wachsenden Erfahrung
profitieren.
19Source-Code enthält das Design
- Refactoring ändert das Design schrittweise im
Quelltext. Sonstige Design-Dokumentation bremst
dabei. - Keine Hackerei Anforderung an den Code
- Je feiner die übrige Designdokumentation ist,
desto problematischer - Design Freeze schließt Refactoring aus
20Einfachheit als Wert
- Softwareentwicklung ist eine der kompliziertesten
Tätigkeiten der Welt. - Quelltexte sind primär für menschliche Leser
- Möglichst viele Hilfestellungen
- Unnötige Komplexität entfernen
21Wenn du es siehst, tue es
- Ein Problem lieber gleich angehen als es auf die
lange Bank schieben. - Probleme verschwinden nicht von alleine
- Vorgehensmodell muss das zulassen
- Mut als Wert
- Ausnahme kurz vor einer Deadline.
22Qualität als Wert?
- Qualität als Wert entwickelt leicht eine
Eigendynamik. - Qualität ist relativ zu Maßstäben damit lässt
sich Vieles begründen - Stattdessen klarer Einfachheit und
Kommunikation
23Versionsverwaltung
- Eine Versionsverwaltung ist eine wichtige
Voraussetzung für Refactoring, besonders im Team. - Reversibilität von Refactorings
- Kurze Check-In-Zyklen
24Buildprozess
- Nur ein wohldefinierter Buildprozess erlaubt die
Überprüfung von Änderungen. - An jedem Arbeitsplatz verfügbar
- Muss gelebt werden ? Integration in IDE
- Früh aufsetzen und mit dem Projekt wachsen lassen
25Häufige Integration
- Es muss immer ein funktionstüchtiger Stand des
Systems verfügbar sein. - Systemteile früh aneinanderschrauben, damit sie
nicht auseinanderlaufen - z.B. nächtlicher Build
26Einwände gegen Refactoring
- Es gibt teilweise tatsächliche Hindernisse
- Design ist nicht mehr dokumentiert.
- Refactoring lohnt sich nicht.
- Wo sind die kurzfristigen Vorteile?
- Es könnte etwas kaputtgehen.
27Design ist nicht dokumentiert
- Einwand Durch Refactoring laufen Implementierung
und Designdokumentation auseinander - Im sehr großen Maßstab berechtigter Einwand
- Ansonsten
- Gesonderte Designdokumentation veraltet ohnehin
- Quelltext gewinnt an Klarheit Design wird im
Quelltext expliziter
28Refactoring lohnt sich nicht
- Einwand Während des Refactorings implementiert
man keine Funktionalität. - Durch Refactoring gleichbleibend gutes Design
- System bleibt änderbar
- Nicht ästhetische Selbstbefriedigung
29Keine kurzfristigen Vorteile
- Einwand Refactoring bringt langfristig Vorteile,
aber nicht kurzfristig. - Refactoring als Teil des jeweiligen Arbeitspakets
- Kein Hauruck-Redesign, sondern hier ein wenig und
dort ein wenig. - Vereinfacht die tägliche Arbeit und spart dabei
Zeit.
30Es könnte etwas kaputt gehen
- Einwand Refactoring könnte bestehende
Funktionalität kaputt machen. - JUnit-Tests
- In kleinen Schritten vorgehen
- Bei Unsicherheit lieber vorsichtig sein
- Andererseits bei jeder Änderung kann etwas
kaputtgehen...
31Alternative Einfach tun
- Wenn das Management nicht von Refactoring
überzeugt ist, gibt es die Möglichkeit, es
einfach zu tun - Der offizielle Weg ist besser
- Professionelle Verantwortung
- Es spart Zeit, wird sich also bewähren
32Grenzen des Refactoring
- Es gibt Situationen, wo Refactoring nicht gut
funktioniert. - Relationale Datenbanken
- Fixierte Schnittstellen
- Hoffnungslos kaputtes System
33Zusammenfassung
- Das Umfeld des Refactoring
- Agile Prozesse erlauben es, spät Änderungen am
System durchzuführen - Versionsverwaltung, Build Management
- Es gibt Einwände, mit denen man sich auseinander
setzen muss - Refactoring ist lernbar Keine Scheu!
34Übersicht
- Einführung
- Das Umfeld für Refactoring
- ? Code Smells als Wegweiser
- Automatisierte Tests mit JUnit
- Konkrete Refactorings
- Beispiel
- Toolunterstützung
35Wann Refactoring?
- Das wie ist relativ einfach, aber wann und wo
soll man refaktorieren? - Lohnt sich ein bestimmtes Refactoring?
- In welche Richtung soll man gehen?
- Wo fängt man an?
- Wann soll man aufhören?
36Code Smells
- Geruch von Quelltexten ist eine Metapher, um
über ihre Qualität zu reden. - Katalog von Gerüchen
- Keine präzisen Kriterien
- Hilfestellung für die Intuition
37Duplizierter Code
- Wenn das Gleiche an zwei Stellen im Quelltext
steht, stinkt das zum Himmel. - Der Quelltext ist unübersichtlich
- Das System ist schwer zu ändern
- Inkonsistenzen und damit Fehler schleichen sich
ein
38Datenklasse
- Eine Klasse, die nur Daten und keine Logik
enthält, ist ein Indiz für Verbesserungs-potential
- Oft gibt es Funktionalität, die im Wesentlichen
auf diesen Daten operiert - Andernfalls ist vielleicht die Klasse schlecht
geschnitten
39Kommentare
- Kommentare an sich riechen gut, sie werden aber
oft als Deodorant verwendet. - Kommentare sind Zeichen, dass der Quelltext
selbst nicht klar verständlich ist - Kommentare können veralten oder in die Irre führen
40Unangebrachte Intimität
- Zwei Klassen, die ausgiebig gegenseitig Methoden
aufrufen, sind oft nicht gut geschnitten. - Sie sind eng gekoppelt und schwierig unabhängig
voneinander zu ändern - Sie sind schwierig zu benutzen, weil sie keine
klare Aufgabenteilung haben
41Neid
- Eine Methode, die im Wesentlichen auf Attributen
einer anderen Klasse operiert, ist dort
wahrscheinlich besser aufgehoben. - Die Signatur der Methode wird dann einfacher und
die Aufgabenteilung der Klassen natürlicher
42Switch
- Fallunterscheidungen mit switch führen oft zu
doppeltem Code, weil die gleichen Fälle mehrmals
unterschieden werden. - Switch ist nicht typsicher
- Man vergisst leicht Fälle
- Zusätzliche Fälle müssen an vielen Stellen
bedacht werden
43Lange Methode
- Lange Methoden sind aufwendiger zu verstehen als
kurze. - Kurze Methoden können durch ihre Namen den
Quelltext dokumentieren - Durch Extrahieren kurzer Methoden kann man
Code-Duplizierung vermeiden und Aufgaben
zwischen Klassen verschieben
44Monster-Klasse
- Eine zu große Klasse wird unübersichtlich.
- Zu viele Attribute führen leicht zu
Code-Duplizierung
45Datenklumpen
- Eine Gruppe von Daten, die oft zusammen vorkommt,
kann man oft als Klasse zusammenfassen. - Dieser Typ kann Funktionalität bekommen und
dadurch doppelten Code vermeiden - Die Verwendung der Daten wird einfacher
?
46Unechte Primitive Datentypen
- Primitive Datentypen können oft besser durch
Klassen ersetzt werden. - Eigene Werttypen sind typsicher und dokumentieren
den Code - Manchmal gibt es falsche Scheu vor kleinen
Klassen
?
47Schrotkugeln herausoperieren
- Wenn man viele Klassen ändern muss, um einen
Aspekt zu ändern, ließe er sich vielleicht an
einer Stelle lokalisieren. - Man übersieht sonst leicht eine Stelle
- Es entsteht leicht doppelter Code
48Kombinierte Abstraktionen
- Wenn eine Klasse von vielen verschiedenen
Änderungen betroffen wird, ist es vielleicht
besser, sie zu spalten. - Sonst betreffen Änderungen potentiell mehr
Quelltext als nötig - Aufteilung macht den Code übersichtlicher
49Lange Parameterliste
- Eine lange Parameterliste deutet auf
Verbesserungspotential hin. - Lange Parameterlisten sind unübersichtlich
- Sie neigen dazu, sich zu ändern
- Attribute oder Parameterobjekte sind besser
50Faule Klasse
- Eine Klasse, die fast nichts mehr tut, kann mehr
im Weg sein als nützen. - Jede Klasse bedeutet Komplexität
- Der Nutzen kann kleiner werden als der Preis
51Hellseherei
- Oft berücksichtigen Leute sicherheitshalber
Erweiterungen, die eventuell später benötigt
werden. - Das Design wird komplizierter, schwieriger zu
verstehen und ändern - Indiz Eine Methode oder Klasse wird nur von
Tests verwendet
52Methodenketten
- Wenn man erst eine Reihe von get-Methoden
aufrufen muss, um das eigentlich interessante
Objekt zu bekommen, durchbricht das die
Kapselung. - Änderungen der dazwischenliegenden Klassen
betreffen den Client
53Vermittler
- Wenn viele Methoden einer Klasse den Aufruf
einfach nur durchreichen, macht das das Interface
unnötig kompliziert. - Der Client sollte stattdessen direkt mit dem
inneren Objekt reden
54Alternative Klassen mit verschiedenen
Schnittstellen
- Wenn mehrere Klassen das Gleiche tun, bedeutet
das Code-Duplizierung mit allen ihren Problemen. - Das gilt insbesondere bei unterschiedlichen
Interfaces
55Ausgeschlagenes Erbe
- Wenn eine Unterklasse wesentliche Teile der
Basisklasse ignoriert, irritiert das menschliche
Leser. - Wenn Implementierung ignoriert wird, ist das
nicht so schlimm - Wenn Teile der Schnittstelle nicht unterstützt
werden, ist die Vererbung falsch
56Übersicht
- Einführung
- Das Umfeld für Refactoring
- Code Smells als Wegweiser
- ? Automatisierte Tests mit JUnit
- Konkrete Refactorings
- Beispiel
- Toolunterstützung
57Automatisierte Modultests
- Automatisierte Modultests reduzieren das Risiko
beim Refactoring. - automatisiert Die Tests laufen auf Knopfdruck ab
und brauchen keine Nutzeraktion. - Modultests Getestet werden die einzelnen Klassen.
58JUnit
- JUnit ist ein Framework zur Unterstützung von
automatisierten Modultests. - Tests sind Java-Klassen
- Test-Schreiben ist kein großer Overhead
- www.junit.org
59Beispiel
- Ein dummes aber einfaches Beispiel...
- Der Quelltext ist im Internet verfügbar
www.haase-consulting.com/download/oop2002
60Integration in IDE
- Der Aufruf von JUnit ist einfach.
- junit.jar in den ClassPath
- junit.swingui.TestRunner als Main-Klasse
- Den Namen des Testfalls als Kommandozeilenparamete
r
61Eine Testklasse je Klasse
- Typischerweise schreibt man zu jeder Klasse eine
Testklasse. - Testklasse erbt von junit.framework.TestCase
- Per Namenskonvention endet der Name der
Testklasse mit Test
TestCase
BriefmarkeTest
Briefmarke
62Tests
- Zu einer Test-Klasse gehören mehrere
Test-Methoden. - Namenskonvention public void test... ()
- Die Reihenfolge der Ausführung steht nicht fest
- Die Testmethoden müssen unabhängig voneinander
sein
63Assertions
- Die eigentlichen Tests erfolgen als Aufrufe der
assert...-Methoden von TestCase. - Wenn die Bedingung erfüllt ist, passiert nichts
- Wenn die Bedingung nicht erfüllt ist, wird
dieser Test abgebrochen und als fehl-geschlagen
vorgemerkt
64TestSuite
- Mehrere Tests können als TestSuite
zusammengefasst werden. - Klasse junit.framework.TestSuite
- Eine TestSuite kann TestCases und TestSuites
enthalten
1..
Test
TestCase
TestSuite
65Beispiel (2)
- Eine zweite Klasse mit Tests kommt dazu.
- Der Quelltext ist im Internet verfügbar
www.haase-consulting.com/download/oop2002
66Test vor Implementierung
- Man kann den Test schon beim Nachdenken über die
geplante Funktionalität schreiben. - Kleine Schritte Ein wenig Testen, ein wenig
Implementieren usw. - Wenn der Test erfolgreich durchläuft, ist man
fertig
67Erst fehlschlagen lassen
- Das Ausprobieren des Tests vor der
Implementierung gibt Sicherheit. - Vielleicht klappt er ja schon...
- Man stellt sicher, dass er tatsächlich ausgeführt
wird
68Tests als Dokumentation
- Die Tests dokumentieren die getesteten Klassen
genau. - Sie zeigen die nötige Initialisierung
- Sie enthalten ein Beispiel für jede Methode
- Die Dokumentation ist nie veraltet
69Beispiel (3)
- Das Beispiel wird noch einmal erweitert.
- Der Quelltext ist im Internet verfügbar
www.haase-consulting.com/download/oop2002
70Tests als Sicherheitsnetz
- Gute Tests schützen davor, unbeabsichtigt
Funktionalität zu ändern, auf die sich andere
Klassen verlassen. - Das ist eine Voraussetzung für zuversichtliches
Refactoring - Es hilft auch bei Erweiterungen und Änderungen
71Wie fängt man an?
- Tests Schreiben braucht Übung, aber
- der Einstieg ist leicht
- eine unvollständige Test-Suite ist hilfreich und
kann organisch wachsen - durch Refactoring kann man später die Tests
verbessern - Testen macht Spaß
72Zusammenfassung
- Refactoring
- Das Umfeld von Refactoring
- Code Smells
- JUnit
73Übersicht
- Einführung
- Das Umfeld für Refactoring
- Code Smells als Wegweiser
- Automatisierte Tests mit JUnit
- ? Konkrete Refactorings
- Beispiel
- Toolunterstützung
74Katalog
- Der Katalog ist Hilfestellung.
- Ziel ist besserer Geruch Eigenes Urteil!
- Probieren und Fehler sind ungefährlich
- JUnit-Tests
- Man kann alles rückgängig machen
- Einstieg Problem
- Konkrete Schritte damit man nichts vergisst
- Am Ende Immer compilieren und testen
75Change Inconsistent Layout
- Quelltext ist inkonsistent formatiert,
insbesondere eingerückt. - Layout an Quelltextrichtlinien anpassen
- Möglichst toolgestützt
- NICHT den eigenen Geschmack aufzwingen!
76Replace Magic Number with Symbolic Constant
- In einer Methode steht eine Zahl (außer 0 und 1).
- Besser eine benannte Konstante einführen
- Name ist Dokumentation
- Wert ist besser änderbar
- Typcode, Arraylänge o.ä.? Dann Alternative wählen
77Replace Temp with Query
- Eine temporäre Variable enthält ein
Zwischenergebnis. - Den entsprechenden Ausdruck in eine Methode
auslagern - Ursprüngliche Methode wird übersichtlicher
- Verfügbar im ganzen Objekt
78Rename Method (1)
- Der Name einer Methode spiegelt nicht (mehr)
ihren Inhalt wieder. - Den Namen ändern
- Analog Parameter hinzufügen oder entfernen
79Rename Method (2)
- Konkrete Schritte
- Basis- und Unterklassen prüfen
- Methode mit neuem Namen anlegen, Implementierung
hineinkopieren, compilieren - Clients schrittweise umstellen, jeweils testen
- Ursprüngliche Methode löschen, Testen
80Replace Constructor with Factory Method
- Konstruktor ist nicht flexibel genug.
- Besser statische create-Methode verwenden
- Kontrolle über Instanzierung (Unterklassen,
Caching, Referenz-Semantik) - Expliziter Name dokumentiert
81Extract Method (1)
- Mehrere Statements einer Methode gehören logisch
zusammen. - Auslagern in eigene Methode
- Sprechender Name für die neue Methode
- Ursprüngliche Methode wird übersichtlicher
82Extract Method (2)
- Konkrete Schritte
- Neue Methode anlegen und gut benennen
- Implementierung hineinkopieren
- Notwendige Parameter anlegen und benennen
- Ggf. Rückgabewert einführen
- Compilieren
- Code in ursprünglicher Methode durch Aufruf
ersetzen
83Inline Method (1)
- Der Code einer Methode ist genauso klar wie ihr
Name. - Methode inlinen
- Unnötige Methode ist nur Ballast
- Gegenstück zu Extract Method
84Inline Method (2)
- Konkrete Schritte
- Prüfen, dass die Methode nicht polymorph ist
- Jeden Aufruf durch den Inhalt ersetzen
- Compilieren und testen
- Methode löschen
- Bei Komplikationen nicht inlinen
85Remove Assignments to Parameters
- Eine Methode enthält eine Zuweisung an einen
Parameter. - Stattdessen temporäre Variable verwenden
- Erhöht Übersichtlichkeit
86Replace Method with Method Object (1)
- Eine Methode verwendet lokale Variablen so, dass
Extract Method schwierig ist. - Neue Klasse anlegen, die der Methode entspricht
- Lokale Variablen werden zu Attributen
- Danach ist Refactoring leicht
87Replace Method with Method Object (2)
- Konkrete Schritte
- Neue Klasse anlegen
- Referenz auf ursprüngliche Klasse sowie Attribute
für die lokalen Variablen einführen - Konstruktor zur Initialisierung
- Methode compute einführen und die ursprüngliche
Implementierung hineinkopieren - In der alten Methode an die neue delegieren
88Substitute Algorithm
- Ein Algorithmus ist komplizierter als nötig.
- Durch einen einfacheren Algorithmus ersetzen
- Insbesondere, wenn zwei verschiedene Algorithmen
für das Gleiche verwendet werden - Optimierung allenfalls am Ende
89Move Method (1)
- Eine Methode benutzt mehr Features oder wird von
mehr Features einer anderen Klasse benutzt als
der, wo sie definiert ist. - Methode zu den Features verschieben
- Stärkere Kohäsion, schlankere Schnittstellen
- Das Original delegiert oder wird gelöscht
90Move Method (2)
- Konkrete Schritte
- Benutzte Felder und Methoden ggf. mitverschieben
- Vererbungshierarchie prüfen
- Methode in der neuen Klasse deklarieren
- Implementierung hineinkopieren und anpassen
- Compilieren
- Aufrufe ändern oder alte Methode umbiegen
91Move Field (1)
- Ein Feld wird in einer anderen Klasse mehr
benutzt - Dorthin verschieben
- Bessere Kohäsion und schlankere Schnittstelle
- Eventuell Methoden mitverschieben
92Move Field (2)
- Konkrete Schritte
- In Zielklasse ein Feld mit get- und set-Methode
anlegen - Navigation von alter zu neuer Klasse
sicherstellen - Ursprüngliches Feld entfernen
- Zugriffe umbiegen (inkl. Unterklassen)
- Compilieren und testen
93Extract Class (1)
- Eine Klasse macht die Arbeit von zweien.
- Neue Klasse anlegen und Felder und Methoden
hineinverschieben - Übersichtlichkeit durch klare Aufgabenteilung
94Extract Class (2)
- Konkrete Schritte
- Aufteilung planen
- Neue Klasse anlegen, Referenz von alter zu neuer
- Einzelne Felder und Methoden verschieben
- Schnittstellen überarbeiten
- Ggf. neue Klasse veröffentlichen (Referenz/Wert)
95Hide Delegate
- Ein Client holt sich von einem Objekt ein anderes
Objekt und ruft darauf eine Methode auf. - Im ersten Objekt eine Methode erzeugen, die dann
delegiert - Dadurch bessere Kapselung
96Remove Middle Man
- Eine Klasse delegiert eine Reihe von Aufrufen
einfach an ein anderes Objekt. - Stattdessen inneres Objekt herausreichen
- get-Methode einführen
- Dadurch Übersichtlichkeit erhöhen
97Replace Data Value with Object
- Ein primitives Attribut hat eigentlich
dazugehörige Funktionalität. - Spezielle Klasse schaffen und deren Instanz
verwenden - Clients nach und nach umstellen
- Wert-Semantik Identität spielt keine Rolle
98Replace Type Code with Subclass (1)
- Eine Klasse hat einen Typcode, der über das
Verhalten entscheidet. - Für jeden Wert des Typcodes eine Unterklasse
- Grundlage für weitere Refactorings
99Replace Type Code with Subclass (2)
- Konkrete Schritte
- Auf Typcode nur durch get-Methode zugreifen
- statische create-Methode einführen
- Für jeden Wert eine Unterklasse einführen, die
die get-Methode überschreibt - Typcode-Attribut aus der Basisklasse entfernen
100Replace Type Code with State/Strategy (1)
- Ein Typ-Code entscheidet über Verhalten, aber es
gibt schon Unterklassen. - Zweite Vererbungshierarchie für Typ-Code
- Als State/Strategy anbinden
101Replace Type Code with State/Strategy (2)
- Konkrete Schritte
- Den Typ-Code hinter get-Methode kapseln
- Neue Klasse mit Unterklassen für jeden Wert
schaffen (State-Klasse) - Typ-Code in die neue Klassenhierarchie
verschieben - Beim Setzen des Typ-Codes stattdessen State
ändern - Typ-Feld entfernen
102Replace Conditional with Polymorphism (1)
- Verhalten wird über einen Typ-Code ausgewählt.
- Für jeden Typcode eine Unterklasse
- Spezifisches Verhalten jeweils in einer
überschriebenen Methode - Man vergisst keine Fälle und gruppiert Logik
103Replace Conditional with Polymorphism (2)
- Konkrete Schritte
- Replace Type Code with ...
- Ggf. Fallunterscheidung in eigene Methode
- Der Reihe nach
- Für Unterklassen die Methode überschreiben und
testen - Den Fall aus der ursprünglichen Methode entfernen
und testen - Methode in der Basisklasse abstrakt machen
104Replace Subclass with Fields (1)
- Unterklassen unterscheiden sich nur in Methoden,
die konstante Werte liefern. - Die Unterklassen entfernen und die Werte aus
Attributen holen - Die Unterklassen sind unnötiger Ballast geworden
105Replace Subclass with Fields (2)
- Konkrete Schritte
- Statische create-Methode einführen
- final Felder in der Basisklasse einführen mit
protected Konstruktor, der sie initialisiert - Unterklassen so ändern, dass sie diesen aufrufen
- Die Methoden in der Basisklasse ändern, so dass
sie die Werte der Felder liefern - Unterklassen entfernen
106Consolidate Duplicate Conditional Fragments
- Ein gemeinsames Stück Code ist in allen Zweigen
einer Fallunterscheidung. - Gemeinsamen Code herausziehen
- Entweder davor oder dahinter
- Sonst eventuell in eigene Methode
107Remove Control Flag
- Ein Flag kontrolliert den Ablauf einer Schleife.
- Besser break, continue und return verwenden
- Kürzer und übersichtlicher
108Replace Nested Conditional with Guard Clause
- Eine Methode hat verschachtelte if-Statements, um
ohne zweites return auszukommen. - Sonderfälle am Anfang behandeln und dort direkt
zurückkehren - Prüflogik in eigene Methoden auslagern
- Normalfall wird explizit
109Separate Query from Modifier (1)
- Eine Methode führt eine Abfrage durch und ändert
gleichzeitig den Zustand des Objekts. - Aufspalten in reine Abfrage und Modifikation
- Reine Abfrage ist vielseitig einsetzbar und
leicht zu verstehen - Optimieren allenfalls später
- Multithreading / Remote Sonderfälle
110Separate Query from Modifier (2)
- Konkrete Schritte
- Die reine Abfrage als eigene Methode bauen
- In der ursprünglichen Methode das Ergebnis der
Abfrage zurückgeben - Compilieren und testen
- In Aufrufen die Abfrage in eigenen Aufruf vor die
alte Methode setzen - Rückgabe der alten Methode entfernen
111Parameterize Method
- Mehrere Methoden tun das Gleiche mit
unterschiedlichen fest verdrahteten Werten. - Zusammenführen in eine einzige Methode mit
Parameter - Vermeidet Code-Duplizierung
112Replace Parameter with Explicit Methods
- Ein Methode macht völlig verschiedene Dinge
abhängig vom Wert eines Parameters. - In mehrere Methoden mit sprechenden Namen zerlegen
113Preserve Whole Object
- Eine Methode bekommt mehrere Attribute des
gleichen Objekts als Parameter. - Besser das ganze Objekt als Parameter
hineinreichen - Außer man will bewusst entkoppeln
114Replace Parameter with Method
- Eine Methode bekommt einen Parameter, den sie
auch selbst ermitteln könnte. - Parameter ermitteln und aus der Signatur
entfernen - Durch Methode von this oder von einem anderen
Parameter
115Introduce Parameter Object (1)
- Mehrere Parameter bilden eine natürliche Einheit.
- Zu einem neuen Typ zusammenfassen
- Wertsemantik
- Typsicher, besser lesbar
116Introduce Parameter Object (2)
- Konkrete Schritte
- Neue Klasse leer anlegen
- Die Parameter als Attribute hinzufügen. Dabei die
Werte immutable machen - Den jeweiligen Parameter aus dem Aufruf
entfernen. Compilieren und Testen - Funktionalität in die neue Klasse extrahieren
117Remove Setting Method
- Ein Feld wird nur bei der Erzeugung gesetzt und
anschließend nicht mehr geändert. - Ggf. Konstruktor einführen
- Set-Methode entfernen
- Feld final machen
118Encapsulate Downcast
- Eine Methode liefert eine Referenz, die erst nach
Downcast verwendbar ist (z.B. ein Element einer
Collection). - Downcast in der Methode durchführen und den
richtigen Typ zurückliefern - Erhöht die Übersichtlichkeit und Typsicherheit
119Pull Up Method
- Unterklassen haben Methoden mit dem gleichen
Ergebnis. - Methoden in die Basisklasse verschieben
- Code-Duplizierung vermeiden
- Eventuell unterschiedliche Algorithmen
120Push Down Method
- Eine Methode der Basisklasse wird nur von einer
Unterklasse verwendet. - Die Methode in diese Unterklasse verschieben
- Macht die Basisklasse einfacher
- Dokumentiert die Abhängigkeiten
121Extract Superclass
- Mehrere Klassen haben teilweise die gleichen
Methoden. - Gemeinsamkeiten in eine Basisklasse ziehen
- Dokumentiert Beziehung zwischen den Klassen
- Öffnet den Blick für Verwendung dieser neuen
Abstraktion
122Form Template Method (1)
- Unterklassen haben Methoden, die ähnliche
Schritte in der gleichen Reihenfolge durchführen. - Methoden extrahieren für Schritte, die von einer
neuen Methode der Basisklasse aufgerufen werden - Vermeidet Code-Duplizierung, dokumentiert
Beziehungen
123Form Template Method (2)
- Konkrete Schritte
- Methoden zerlegen
- Methodensignaturen vereinheitlichen
- Jeweils compilieren und testen
- Ursprüngliche Methode in Basisklasse ziehen
124Replace Inheritance with Delegation
- Eine Unterklasse will nur einen Teil der
Schnittstelle der Basisklasse haben. - Besser Aufrufe an eine Instanz der Basisklasse
durchreichen - Klarere Beziehungen Vererbung legt Polymorphie
nahe
125Der Code ist das Design
- Alle hier präsentierten Refactorings wollen das
Design im Quelltext klarer ausdrücken. - Namen sind sehr wichtig
- Klarheit und Einfachheit
- Compilerunterstützung
- Diese Ziele sind wichtiger als das Verwenden
konkreter Refactorings!
126Übersicht
- Einführung
- Das Umfeld für Refactoring
- Code Smells als Wegweiser
- Automatisierte Tests mit JUnit
- Konkrete Refactorings
- ? Beispiel
- Toolunterstützung
127Beispiel
- Ein praktisches Beispiel der Refactorings
- Der Quelltext ist im Internet verfügbar
www.haase-consulting.com/download/oop2002
128Übersicht
- Einführung
- Das Umfeld für Refactoring
- Code Smells als Wegweiser
- Automatisierte Tests mit JUnit
- Konkrete Refactorings
- Beispiel
- ? Toolunterstützung
129Toolunterstütztes Refactoring
- Die Durchführung des Refactoring lässt sich
weitgehend automatisieren. - Schutz vor Flüchtigkeitsfehlern
- Mehr als Suchen und Ersetzen
- Plausibilitätsprüfungen
130Kriterien für Toolauswahl
- Folgende Punkte stellen Kriterien für die Auswahl
eines Refactoring-Tools dar - Zuverlässigkeit
- Integration mit IDE
- Auf dem Parse-Baum operieren (Extract Method
als Indiz) - Geschwindigkeit
131Refactoring-Tools
- Hier eine Auswahl von aktuellen Refactoring-Tools
für Java - Idea (www.intellij.com)
- Xrefactory (xref-tech.com/speller)
- Eclipse (www.eclipse.org)
- ...
132Toolunterstützung praktisch
- Demonstration für toolgestütztes Refactoring
- So einfach kann Refactoring in der Praxis sein.
133Literatur
- Folgende Quellen sind gute Startpunktefür eine
Vertiefung - Refactoring, Martin Fowler
- www.refactoring.org
- www.junit.org
- Extreme Programming Explained, Kent Beck
134Zusammenfassung
- Refactoring
- Das Umfeld von Refactoring
- Code Smells
- JUnit
- Refactoring-Katalog
- Toolunterstützung