1.35k likes | 1.51k Views
Refactoring Java Code. Arno.Haase@Haase-Consulting.com Arno.Haase@acm.org www.Haase-Consulting.com. Übersicht. Einführung Das Umfeld für Refactoring „Code Smells“ als Wegweiser Automatisierte Tests mit JUnit Konkrete Refactorings Beispiel Toolunterstützung.
E N D
Refactoring Java Code Arno.Haase@Haase-Consulting.com Arno.Haase@acm.org www.Haase-Consulting.com
Übersicht Einführung • Das Umfeld für Refactoring • „Code Smells“ als Wegweiser • Automatisierte Tests mit JUnit • Konkrete Refactorings • Beispiel • Toolunterstützung
„Refactoring“, Martin Fowler • Dieses Tutorial beruht im Wesentlichen auf dem Buch „Refactoring – Improving the Design of Existing Code“ von Martin Fowler. • Standardwerk • Abweichungen im Detail
Refactoring: Definition „Refactoring ist die Verbesserung der Qualität von vorhandenem Quell- text ohne Veränderung der Funktionalität.“
Problem • 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
Refactoring 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
Demonstration • Ein Quelltext sagt mehr als tausend Folien... • Der Quelltext ist im Internet verfügbar: www.haase-consulting.com/download/oop2002
Was 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
Wann 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
Kleine 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
Die 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
Zusammenfassung • 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.
Übersicht • Einführung Das Umfeld für Refactoring • „Code Smells“ als Wegweiser • Automatisierte Tests mit JUnit • Konkrete Refactorings • Beispiel • Toolunterstützung
Altersschwäche • Software kann an Altersschwäche sterben. • Design wächst nicht mit den Anforderungen • Wartung als Albtraum • Manchmal schon vor Inbetriebnahme...
Hellseherei • 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)
Traditionelles Vorgehen • Das Wasserfall-Modell ist immer noch sehr verbreitet. • Änderungen werden mit der Zeit teurer • Risiko minimieren
Ä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
Kriterien 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.
Source-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
Einfachheit 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
Wenn 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.
Qualitä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“
Versionsverwaltung • Eine Versionsverwaltung ist eine wichtige Voraussetzung für Refactoring, besonders im Team. • Reversibilität von Refactorings • Kurze Check-In-Zyklen
Buildprozess • 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
Hä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
Einwä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.“
„Design 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
„Refactoring lohnt sich nicht“ • Einwand: Während des Refactorings implementiert man keine Funktionalität. • Durch Refactoring gleichbleibend gutes Design • System bleibt änderbar • Nicht ästhetische Selbstbefriedigung
„Keine 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.
„Es 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...
Alternative: 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
Grenzen des Refactoring • Es gibt Situationen, wo Refactoring nicht gut funktioniert. • Relationale Datenbanken • Fixierte Schnittstellen • Hoffnungslos kaputtes System
Zusammenfassung • 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!
Übersicht • Einführung • Das Umfeld für Refactoring „Code Smells“ als Wegweiser • Automatisierte Tests mit JUnit • Konkrete Refactorings • Beispiel • Toolunterstützung
Wann 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?
„Code 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
Duplizierter 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
Datenklasse • 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
Kommentare • 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
Unangebrachte 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
Neid • 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
Switch • 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
Lange 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
Monster-Klasse • Eine zu große Klasse wird unübersichtlich. • Zu viele Attribute führen leicht zu Code-Duplizierung
5$ Datenklumpen • 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 +
5$ Unechte 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
Schrotkugeln 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
Kombinierte 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
Lange Parameterliste • Eine lange Parameterliste deutet auf Verbesserungspotential hin. • Lange Parameterlisten sind unübersichtlich • Sie neigen dazu, sich zu ändern • Attribute oder Parameterobjekte sind besser
Faule 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