Refactoring - Martin Fowler - E-Book

Refactoring E-Book

Martin Fowler

0,0

Beschreibung

  • Umfassend überarbeitete und aktualisierte Neuauflage des Standardwerks in vollständig neuer Übersetzung
  • Verbesserungsmöglichkeiten von bestehender Software anhand von Code-Smells erkennen und Code effizient überarbeiten
  • Umfassender Katalog von Refactoring-Methoden mit Code-Beispielen in JavaScript

Seit mehr als zwanzig Jahren greifen erfahrene Programmierer rund um den Globus auf dieses Buch zurück, um bestehenden Code zu verbessern und leichter lesbar zu machen sowie Software besser warten und erweitern zu können.

In diesem umfassenden Standardwerk zeigt Ihnen Martin Fowler, was die Vorteile von Refactoring sind, wie Sie verbesserungsbedürftigen Code erkennen und wie Sie ein Refactoring – unabhängig von der verwendeten Programmiersprache – erfolgreich durchführen. In einem umfangreichen Katalog gibt Fowler Ihnen verschiedene Refactoring-Methoden mit ausführlicher Erläuterung, Motivation, Vorgehensweise und einfachen Beispielen in JavaScript an die Hand.

Darüber hinaus behandelt er insbesondere folgende Schwerpunkte:

  • Allgemeine Prinzipien und Durchführung des Refactorings
  • Refactoring anwenden, um die Lesbarkeit, Wartbarkeit und Erweiterbarkeit von Programmen zu verbessern
  • Code-Smells erkennen, die auf Verbesserungsmöglichkeiten durch Refactoring hinweisen
  • Entwicklung zuverlässiger Tests für das Refactoring
  • Erkennen von Fallstricken und notwendigen Kompromissen bei der Durchführung eines Refactorings

Diese vollständig neu übersetzte Ausgabe wurde von Grund auf überarbeitet, um den maßgeblichen Veränderungen der modernen Programmierung Rechnung zu tragen. Sie enthält einen aktualisierten Katalog von Refactoring-Methoden sowie neue Beispiele für einen funktionalen Programmieransatz.

Aus dem Inhalt:
  • Definition und Grund-lagen von Refactoring
  • Der richtige Zeitpunkt für ein Refactoring
  • Verbesserungsbedürftigen Code erkennen (Code-Smells)
  • Tests und selbsttestender Code
  • Umfangreicher Refactoring-Katalog:
    • Kapselung
    • Verschiebungen
    • Daten organisieren
    • Bedingungen vereinfachen
    • Refactoring von APIs
    • Umgang mit Vererbung
Amazon-Leserstimme zur Vorauflage:

»Dieses Buch gibt einen sehr guten Einstieg in das Refactoring und hält auch eine übersichtliche, gut erklärte Sammlung von Refactoring-Patterns parat. Ein Buch, das sich jeder Programmierer unters Kopfkissen legen sollte.«

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 563

Veröffentlichungsjahr: 2020

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Inhaltsverzeichnis
Impressum
Vorwort zur ersten Auflage
Über den Fachkorrektor der deutschen Ausgabe
Einleitung
Was ist Refactoring?
Worum geht es in diesem Buch?
Wer sollte dieses Buch lesen?
Von anderen geschaffene Grundlagen
Danksagungen
Kapitel 1: Refactoring: Ein erstes Beispiel
1.1 Der Ausgangspunkt
1.2 Anmerkungen zum ersten Programm
1.3 Der erste Schritt des Refactorings
1.4 Aufteilung der statement-Funktion
1.4.1 Entfernen der ‌Variable play
1.4.2 Extrahieren der Berechnung von Treuepunkten
1.4.3 Entfernen der Variable format
1.4.4 Entfernen der Gesamtzahl der Treuepunkte
1.5 Status: Eine Menge verschachtelter Funktionen
1.6 Die Phasen Berechnung und Formatierung voneinander trennen
1.7 Status: Aufgeteilt in zwei Dateien (und Phasen)
1.8 Neuorganisation der Berechnungen nach Art
1.8.1 Berechnen der Kosten einer Vorstellung
1.8.2 Funktionen in die Klasse verschieben
1.8.3 Polymorphes Verhalten der Klasse PerformanceCalculator
1.9 Status: Erzeugen der Daten mit der polymorphen Klasse PerformanceCalculator
1.10 Abschließende Überlegungen
Kapitel 2: Prinzipien des Refactorings
2.1 Refactoring: Definition
2.2 Die beiden Hüte
2.3 Gründe für ein Refactoring
2.3.1 Refactoring verbessert das Design der Software
2.3.2 Refactoring macht Software besser verständlich
2.3.3 Refactoring hilft mir, Bugs aufzuspüren‌
2.3.4 Refactoring hilft mir, schneller zu programmieren
2.4 Der richtige Zeitpunkt für ein Refactoring‌
2.4.1 Vorbereitendes Refactoring: Hinzufügen eines Features vereinfachen
2.4.2 Refactoring zwecks Verbesserung der Verständlichkeit des Codes
2.4.3 Refactoring als Abfallentsorgung
2.4.4 Geplantes ‌Refactoring und spontanes Refactoring
2.4.5 Langfristiges Refactoring
2.4.6 Refactoring beim Code-Review‌
2.4.7 Wie sag ich’s meinem Chef?
2.4.8 Wann sollte man kein ‌Refactoring durchführen?
2.5 Probleme beim Refactoring
2.5.1 Verlangsamung der Entwicklung neuer Features
2.5.2 Code-Eigentümerschaft
2.5.3 Entwicklungszweige
2.5.4 Testen
2.5.5 Legacy Code‌
2.5.6 Datenbanken
2.6 Refactoring, Architektur und Yagni
2.7 Refactoring und der Softwareentwicklungsprozess
2.8 Refactoring und Performance
2.9 Ursprünge des Refactorings
2.10 Automatisiertes Refactoring‌
2.11 Weiterführende Literatur
Kapitel 3: Code-Smells: Schlechte Gerüche im Code
3.1 Rätselhafte Bezeichnung
3.2 Redundanter Code
3.3 Lange Funktion‌
3.4 Lange Parameterliste‌
3.5 Globale Daten‌
3.6 Veränderliche Daten‌
3.7 Divergierende Änderungen
3.8 Chirurgie mit der Schrotflinte
3.9 Feature-Neid
3.10 Datenklumpen
3.11 Obsession für elementare Datentypen
3.12 Wiederholte switch-Anweisungen
3.13 Schleifen
3.14 Träges Element
3.15 Spekulative Generalisierung
3.16 Temporäres Feld
3.17 Mitteilungsketten
3.18 Vermittler‌
3.19 Insiderhandel
3.20 Umfangreiche Klasse‌
3.21 Alternative Klassen mit unterschiedlichen Schnittstellen
3.22 Datenklasse
3.23 Ausgeschlagenes Erbe
3.24 Kommentare‌
Kapitel 4: Tests erstellen
4.1 Der Nutzen selbsttestenden Codes‌‌
4.2 Beispielcode zum Testen
4.3 Ein erster Test
4.4 Hinzufügen eines weiteren Tests
4.5 Ändern des Test-Fixtures
4.6 Austesten der Grenzen‌
4.7 Und noch viel mehr
Kapitel 5: Der Katalog
5.1 Format der Refactorings
5.2 Die Auswahl der Refactorings
Kapitel 6: Eine erste Zusammenstellung von Refactorings
6.1 Funktion extrahieren (Extract Function‌)
6.1.1 Motivation
6.1.2 Vorgehen
6.1.3 Beispiel: Keine Variablen außerhalb des Gültigkeitsbereichs
6.1.4 Beispiel: Verwendung lokaler Variablen
6.1.5 Beispiel: Zuweisung zu einer lokalen Variable
6.2 Funktion inline platzieren (Inline Function)
6.2.1 Motivation
6.2.2 Vorgehen
6.2.3 Beispiel
6.3 ‌Variable extrahieren (Extract Variable‌)
6.3.1 Motivation
6.3.2 Vorgehen
6.3.3 Beispiel
6.3.4 Beispiel mit einer Klasse
6.4 ‌Variable inline platzieren (Inline Variable‌)
6.4.1 Motivation
6.4.2 Vorgehen
6.5 ‌Funktionsdeklaration ändern (Change Function Declaration‌)
6.5.1 Motivation
6.5.2 Vorgehen
6.5.3 Beispiel: Umbenennen einer Funktion (einfaches Vorgehen)
6.5.4 Beispiel: Umbenennen einer Funktion (Migrationsansatz)
6.5.5 Beispiel: Hinzufügen eines Parameters‌
6.5.6 Beispiel: Einen Parameter durch eine seiner Eigenschaften ersetzen‌
6.6 ‌Variable kapseln (Encapsulate Variable‌)
6.6.1 Motivation
6.6.2 Vorgehen
6.6.3 Beispiel
6.7 ‌Variable umbenennen‌ (Rename Variable‌)
6.7.1 Motivation
6.7.2 Vorgehen
6.7.3 Beispiel
6.8 ‌Parameterobjekt einführen (Introduce Parameter Object)
6.8.1 Motivation
6.8.2 Vorgehen
6.8.3 Beispiel
6.9 ‌Funktionen zu einer Klasse vereinen (Combine Functions into Class‌)
6.9.1 Motivation
6.9.2 Vorgehen
6.9.3 Beispiel
6.10 ‌Funktionen zu einer Transformation vereinen (Combine Functions into Transform‌)
6.10.1 Motivation
6.10.2 Vorgehen
6.10.3 Beispiel
6.11 ‌Phase aufteilen (Split Phase)
6.11.1 Motivation
6.11.2 Vorgehen
6.11.3 Beispiel
Kapitel 7: Kapselung
7.1 Datensatz kapseln (Encapsulate Record)
7.1.1 Motivation
7.1.2 Vorgehen
7.1.3 Beispiel
7.1.4 Beispiel: Kapselung eines verschachtelten Datensatzes
7.2 Collection kapseln (Encapsulate Collection‌)
7.2.1 Motivation
7.2.2 Vorgehen
7.2.3 Beispiel
7.3 ‌Elementaren Wert durch Objekt ersetzen (Replace Primitive with Object‌)‌
7.3.1 Motivation
7.3.2 Vorgehen
7.3.3 Beispiel
7.4 ‌Temporäre Variable durch Abfrage ersetzen (Replace Temp with Query)
7.4.1 Motivation
7.4.2 Vorgehen
7.4.3 Beispiel
7.5 ‌Klasse extrahieren (Extract Class)
7.5.1 Motivation
7.5.2 Vorgehen
7.5.3 Beispiel
7.6 Klasse inline platzieren (Inline Class)
7.6.1 Motivation
7.6.2 Vorgehen
7.6.3 Beispiel
7.7 ‌Delegation verbergen (Hide Delegate‌)
7.7.1 Motivation
7.7.2 Vorgehen
7.7.3 Beispiel
7.8 ‌Vermittler entfernen (Remove Middle Man)
7.8.1 Motivation
7.8.2 Vorgehen
7.8.3 Beispiel
7.9 ‌Algorithmus ersetzen (Substitute Algorithm‌)
7.9.1 Motivation
7.9.2 Vorgehen
Kapitel 8: Verschiebungen
8.1 Funktion verschieben (Move Function‌)
8.1.1 Motivation
8.1.2 Vorgehen
8.1.3 Beispiel: Verschieben einer verschachtelten Funktion in die oberste Ebene
8.1.4 Beispiel: Verschiebungen zwischen Klassen
8.2 ‌Feld verschieben (Move Field)
8.2.1 Motivation
8.2.2 Vorgehen
8.2.3 Beispiel
8.2.4 Beispiel: Verschiebung in ein gemeinsam genutztes Objekt
8.3 ‌Anweisungen in ‌Funktion verschieben (Move Statements into Function‌)
8.3.1 Motivation
8.3.2 Vorgehen
8.3.3 Beispiel
8.4 ‌Anweisungen in Aufrufer verschieben (Move Statements to Caller)
8.4.1 Motivation
8.4.2 Vorgehen
8.4.3 Beispiel
8.5 ‌Inline-Code durch Funktionsaufruf ersetzen (Replace Inline Code with Function Call)
8.5.1 Motivation
8.5.2 Vorgehen
8.6 ‌Anweisungen verschieben (Slide Statements‌)
8.6.1 Motivation
8.6.2 Vorgehen
8.6.3 Beispiel
8.6.4 Beispiel: Verschiebungen und bedingte Anweisungen
8.6.5 Literaturhinweis
8.7 ‌Schleife aufteilen (Split Loop)
8.7.1 Motivation
8.7.2 Vorgehen
8.7.3 Beispiel
8.8 ‌Schleife durch Pipeline ersetzen (Replace Loop with Pipeline)
8.8.1 Motivation
8.8.2 Vorgehen
8.8.3 Beispiel
8.8.4 Literaturhinweis
8.9 ‌Toten Code entfernen (Remove Dead Code‌)
8.9.1 Motivation
8.9.2 Vorgehen
Kapitel 9: Daten organisieren
9.1 Variable aufteilen (Split Variable‌)
9.1.1 Motivation
9.1.2 Vorgehen
9.1.3 Beispiel
9.1.4 Beispiel: Zuweisung zu einem Eingabeparameter‌
9.2 ‌Feld umbenennen (Rename Field)‌
9.2.1 Motivation
9.2.2 Vorgehen
9.2.3 Beispiel: Umbenennen eines Feldes
9.3 ‌Abgeleitete Variable durch Abfrage ersetzen (Replace Derived Variable with Query‌)
9.3.1 Motivation
9.3.2 Vorgehen
9.3.3 Beispiel
9.3.4 Beispiel: Mehr als eine Quelle
9.4 ‌Referenz durch Wert ersetzen (Change Reference to Value)
9.4.1 Motivation
9.4.2 Vorgehen
9.4.3 Beispiel
9.5 ‌Wert durch Referenz ersetzen (Change Value to Reference)
9.5.1 Motivation
9.5.2 Vorgehen
9.5.3 Beispiel
Kapitel 10: Bedingungen vereinfachen
10.1 Bedingung zerlegen (Decompose Conditional‌)
10.1.1 Motivation
10.1.2 Vorgehen
10.1.3 Beispiel
10.2 ‌Bedingten Ausdruck zusammenfassen (Consolidate Conditional Expression)
10.2.1 Motivation
10.2.2 Vorgehen
10.2.3 Beispiel
10.2.4 Beispiel: Verwendung von »and«
10.3 ‌Verschachtelte Bedingung durch ‌Wächterbedingung ersetzen (Replace Nested Conditional with Guard Clauses)
10.3.1 Motivation
10.3.2 Vorgehen
10.3.3 Beispiel
10.3.4 Beispiel: Umkehrung der Bedingungen
10.4 ‌Bedingung durch Polymorphie ersetzen (Replace Conditional with Polymorphism)‌
10.4.1 Motivation
10.4.2 Vorgehen
10.4.3 Beispiel
10.4.4 Beispiel: Polymorphie für Variationen verwenden
10.5 ‌Sonderfall einführen (Introduce Special Case)
10.5.1 Motivation
10.5.2 Vorgehen
10.5.3 Beispiel
10.5.4 Beispiel: Verwendung eines Objekts mit Literalen
10.5.5 Beispiel: Verwenden einer Transformation
10.6 ‌Assertion einführen (Introduce Assertion)
10.6.1 Motivation
10.6.2 Vorgehen
10.6.3 Beispiel
Kapitel 11: Refactoring von APIs
11.1 ‌Abfrage von Veränderung trennen (Separate Query from Modifier‌)
11.1.1 Motivation
11.1.2 Vorgehen
11.1.3 Beispiel
11.2 ‌Funktion parametrisieren (Parameterize Function)
11.2.1 Motivation
11.2.2 Vorgehen
11.2.3 Beispiel
11.3 ‌Steuerungs-Flag entfernen (Remove Flag Argument)
11.3.1 Motivation
11.3.2 Vorgehen
11.3.3 Beispiel
11.4 ‌Vollständiges Objekt‌ erhalten (Preserve Whole Object)
11.4.1 Motivation
11.4.2 Vorgehen
11.4.3 Beispiel
11.4.4 Beispiel: Eine Variante zum Erstellen der neuen Funktion
11.5 ‌Parameter durch Abfrage ersetzen (Replace Parameter with Query‌)‌
11.5.1 Motivation
11.5.2 Vorgehen
11.5.3 Beispiel
11.6 ‌Abfrage durch Parameter ersetzen (Replace Query with Parameter)
11.6.1 Motivation
11.6.2 Vorgehen
11.6.3 Beispiel
11.7 ‌Setter entfernen (Remove Setting Method)‌
11.7.1 Motivation
11.7.2 Vorgehen
11.7.3 Beispiel
11.8 ‌Konstruktor durch Fabrikfunktion ersetzen (Replace Constructor with Factory Function‌)
11.8.1 Motivation
11.8.2 Vorgehen
11.8.3 Beispiel
11.9 ‌Funktion durch Befehl ersetzen (Replace Function with Command)
11.9.1 Motivation
11.9.2 Vorgehen
11.9.3 Beispiel
11.10 ‌Befehl durch Funktion ersetzen (Replace Command with Function)
11.10.1 Motivation
11.10.2 Vorgehen
11.10.3 Beispiel
Kapitel 12: Der Umgang mit Vererbung
12.1 Methode nach oben verschieben (Pull Up Method‌)‌
12.1.1 Motivation
12.1.2 Vorgehen
12.1.3 Beispiel
12.2 ‌Feld nach oben verschieben (Pull Up Field)‌
12.2.1 Motivation
12.2.2 Vorgehen
12.3 ‌Konstruktorrumpf nach oben verschieben (Pull Up Constructor Body‌)
12.3.1 Motivation
12.3.2 Vorgehen
12.3.3 Beispiel
12.4 ‌Methode nach unten verschieben ‌(Push Down Method)
12.4.1 Motivation
12.4.2 Vorgehen
12.5 ‌Feld nach unten verschieben (Push Down Field)‌
12.5.1 Motivation
12.5.2 Vorgehen
12.6 ‌Typenschlüssel durch Unterklassen ersetzen (Replace Type Code with Subclasses)
12.6.1 Motivation
12.6.2 Vorgehen
12.6.3 Beispiel
12.6.4 Beispiel: Indirekte Vererbung
12.7 ‌Unterklasse entfernen (Remove Subclass)
12.7.1 Motivation
12.7.2 Vorgehen
12.7.3 Beispiel
12.8 ‌Basisklasse extrahieren (Extract Superclass)
12.8.1 Motivation
12.8.2 Vorgehen
12.8.3 Beispiel
12.9 ‌Hierarchie abbauen (Collapse Hierarchy‌)
12.9.1 Motivation
12.9.2 Vorgehen
12.10 ‌Unterklasse durch Delegation ersetzen (Replace Subclass with Delegate)
12.10.1 Motivation
12.10.2 Vorgehen
12.10.3 Beispiel
12.10.4 Beispiel: Ersetzen einer Hierarchie
12.11 ‌Basisklasse durch Delegation ersetzen (Replace Superclass with Delegate‌)
12.11.1 Motivation
12.11.2 Vorgehen
12.11.3 Beispiel
Anhang A: Bibliografie
Anhang B: Liste der Refactorings
Anhang C: Code-Smells

Für Cindy

–Martin

Martin Fowler

Refactoring

Wie Sie das Design bestehender Software verbessern

Mit Beiträgen von Kent Beck

Übersetzung aus dem Englischen von Dr. Jan Linxweiler und Knut Lorenzen

Impressum

Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über <http://dnb.d-nb.de> abrufbar.

ISBN 978-3-95845-943-4 2. Auflage 2020

www.mitp.de E-Mail: [email protected] Telefon: +49 7953 / 7189 - 079 Telefax: +49 7953 / 7189 - 082

Authorized translation from the English language edition, REFACTORING: IMPROVING THE DESIGN OF EXISTING CODE, 2nd Edition by MARTIN FOWLER, published by Pearson Education, Inc, publishing as Addison-Wesley Professional, Copyright © 2019 Pearson Education, Inc.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.

GERMAN language edition published by MITP VERLAGS GMBH & CO. KG

© 2020 mitp Verlags GmbH & Co. KG

Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen.

Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.

Lektorat: Sabine Schulz, Janina Bahlmann Fachkorrektorat: Dr. Jan Linxweiler Sprachkorrektorat: Doris Weißgerber Covergestaltung: Christian Kalkertelectronic publication: III-satz, Husby, www.drei-satz.de Bildnachweis: © liz west / https://www.flickr.com/photos/calliope/3834075344/

Dieses Ebook verwendet das ePub-Format und ist optimiert für die Nutzung mit dem iBooks-reader auf dem iPad von Apple. Bei der Verwendung anderer Reader kann es zu Darstellungsproblemen kommen.

Der Verlag räumt Ihnen mit dem Kauf des ebooks das Recht ein, die Inhalte im Rahmen des geltenden Urheberrechts zu nutzen. Dieses Werk, einschließlich aller seiner Teile, ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheherrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Dies gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und Einspeicherung und Verarbeitung in elektronischen Systemen.

Der Verlag schützt seine ebooks vor Missbrauch des Urheberrechts durch ein digitales Rechtemanagement. Bei Kauf im Webshop des Verlages werden die ebooks mit einem nicht sichtbaren digitalen Wasserzeichen individuell pro Nutzer signiert.

Bei Kauf in anderen ebook-Webshops erfolgt die Signatur durch die Shopbetreiber. Angaben zu diesem DRM finden Sie auf den Seiten der jeweiligen Anbieter.

Vorwort zur ersten Auflage

»Refactoring« wurde ursprünglich in Smalltalk-Kreisen konzipiert. Es dauerte allerdings nicht lange, bis es auch den Weg in die Lager anderer Programmiersprachen fand. Da Refactoring ein integraler Bestandteil bei der Entwicklung von Frameworks ist, kommt dieser Ausdruck häufig zur Sprache, wenn sich »Frameworker« über ihr Handwerk unterhalten. Er wird verwendet, wenn sie ihre Klassenhierarchien überarbeiten und davon schwärmen, wie viele Codezeilen sie löschen konnten. Frameworker wissen, dass ein Framework nicht gleich beim ersten Versuch perfekt ist – vielmehr muss es sich aus ihrer stetig anwachsenden Erfahrung entwickeln. Sie wissen auch, dass Code häufiger gelesen und modifiziert als geschrieben wird. Beim Refactoring handelt es sich um den Schlüssel zu lesbarem und modifizierbarem Code. Das gilt insbesondere für Frameworks, trifft aber auch auf Software im Allgemeinen zu.

Wo also liegt das Problem? Ganz einfach: Refactoring ist riskant. Es erfordert Änderungen an funktionsfähigem Code, die zu schwer aufspürbaren Bugs führen können. Wird ein Refactoring nicht sorgfältig durchgeführt, kann es Sie um Tage oder sogar Wochen zurückwerfen. Und Refactoring wird noch riskanter, wenn es außerhalb eines formellen Rahmens oder gar spontan durchgeführt wird. Zunächst beginnt man an der Oberfläche des Codes zu schürfen. Dabei findet man schnell Verbesserungsmöglichkeiten und beginnt, tiefer zu graben. Je tiefer man gräbt, desto mehr Verbesserungsmöglichkeiten werden aufgedeckt ... und umso mehr Änderungen nimmt man vor. Unter Umständen gräbt man sich dabei ein sehr tiefes Loch, aus dem man nicht wieder herauskommt. Um zu verhindern, dass man sich sein eigenes Grab schaufelt, muss ein Refactoring systematisch durchgeführt werden. Als meine Co-Autoren und ich das Buch Design Patterns verfassten, wiesen wir darauf hin, dass Design Patterns gut geeignete Ziele für Refactorings darstellen. Allerdings ist das Identifizieren des Ziels nur ein Teil des Problems – den Code dementsprechend umzustrukturieren, stellt eine weitere Herausforderung dar.

Martin Fowler und die weiteren mitwirkenden Autoren leisten zur Entwicklung objektorientierter Software einen Beitrag von unschätzbarem Wert, indem sie den Prozess des Refactorings ausführlich darstellen. Dieses Buch erklärt Prinzipien und bewährte Praktiken des Refactorings und zeigt auf, wann und wo Sie beginnen sollten, Ihren Code auf Verbesserungsmöglichkeiten hin zu untersuchen. In der Mitte des Buchs ist eine umfangreiche Liste an Refactorings enthalten. Jedes dieser Refactorings beschreibt die Motivation sowie die jeweilige Vorgehensweise einer bewährten Transformation des Codes. Einige der Refactorings, wie beispielsweise ‌Funktion extrahieren (Extract Function, Abschnitt 6.1) oder ‌Feld verschieben (Move Field, Abschnitt 8.2), scheinen auf der Hand zu liegen.

Aber lassen Sie sich nicht täuschen. Die Vorgehensweisen solcher Refactorings zu verstehen, ist von entscheidender Bedeutung, um ein Refactoring auf disziplinierte Weise auszuführen. Die Refactorings in diesem Buch werden Ihnen dabei helfen, Ihren Code Schritt für Schritt zu modifizieren und auf diese Weise das Risiko bei der Weiterentwicklung Ihres Designs zu verringern. Als Entwickler werden Sie schon bald die Refactorings und ihre Bezeichnungen in Ihren Wortschatz aufnehmen.

Meine ersten Erfahrungen mit diszipliniertem »Schritt-für-Schritt«-Refactoring machte ich während des Pair Programmings mit Kent Beck 30.000 Fuß über dem Erdboden. Er stellte sicher, dass wir die Refactorings aus diesem Buch schrittweise anwendeten. Ich war erstaunt, wie gut dieses Verfahren funktionierte. Ich hatte nicht nur größeres Vertrauen in den geschriebenen Code, ich fühlte mich auch weniger gestresst. Ich empfehle Ihnen nachdrücklich, diese Refactorings auszuprobieren: Ihnen und Ihrem Code wird es damit viel besser ergehen.‌

–Erich Gamma, Object Technology International, Inc.

Januar 1999

Über den Fachkorrektor der deutschen Ausgabe

Dr. Jan Linxweiler ist Geschäftsführer des Centerfor Mechanics, Uncertainty and Simulation in Engineering an der TU Braunschweig. Er verfügt über mehr als 15 Jahre Erfahrung in Forschung und Lehre im Bereich der Softwareentwicklung und war als Softwarearchitekt im Automotive-Sektor tätig. Darüber hinaus entwickelt er erfolgreich Apps für iOS und macOS. Seine Leidenschaft für die Entwicklung hochqualitativer Software vermittelt Linxweiler auch den Studierenden in seinen Vorlesungen.

Einleitung

Es war einmal ein Berater, der sich zu einem Softwareprojekt aufmachte, um sich den Code anzusehen, der im Laufe des Projekts entstanden war. Als er sich die Klassenhierarchie im Kern des Systems ansah, stellte er fest, dass es ein ziemliches Durcheinander war. Klassen auf höherem Abstraktionsniveau gingen von bestimmten Annahmen bezüglich der Funktionsweise anderer Klassen aus – Annahmen, die sie in Form von Code an ihre Unterklassen vererbten. Der Code war allerdings nicht für alle Unterklassen geeignet, daher wurde er ziemlich häufig überschrieben. Schon geringfügige Änderungen an der Basisklasse hätten die Notwendigkeit, den Code zu überschreiben, weitestgehend beseitig. An anderen Stellen hatte man die Intentionen der Basisklasse offenbar nicht richtig verstanden, denn Teile des in der Basisklasse bereits vorhandenen Verhaltens wurden in Unterklassen erneut implementiert. An wieder anderen Stellen erledigten mehrere Unterklassen die gleichen Aufgaben durch Code, der in der Klassenhierarchie eindeutig nach oben verschoben werden konnte.

Der Berater empfahl dem Projektmanagement, den Code zu untersuchen und zu bereinigen – was allerdings nicht gerade für Begeisterung sorgte. Der Code funktionierte offenbar, und es gab erheblichen Termindruck. Das Management meinte, man werde sich später darum kümmern.

Den Programmierern, die an der Klassenhierarchie arbeiteten, hatte der Berater ebenfalls gezeigt, was da tatsächlich vor sich ging. Sie waren eifrig und erkannten das Problem. Ihnen war klar, dass es nicht wirklich ihr Fehler war. Manchmal benötigt es eben ein weiteres Paar an Augen, um ein Problem zu erkennen. Die Programmierer befassten sich also ein paar Tage lang damit, die Hierarchie aufzuräumen. Als sie fertig waren, hatten sie die Hälfte des Codes aus der Hierarchie entfernt, ohne dadurch die Funktionalität einzuschränken. Sie waren mit dem Ergebnis sehr zufrieden und stellten fest, dass sowohl das Hinzufügen neuer Klassen als auch die Verwendung der Klassen des übrigen Systems schneller und einfacher von der Hand gingen.

Das Projektmanagement war nicht erfreut. Der Terminplan war eng, und es gab eine Menge Arbeit zu erledigen. Die beiden Programmierer hatten zwei Tage mit Arbeiten verbracht, die nichts zu den vielen Features beitrugen, die das System in wenigen Monaten besitzen musste. Der alte Code hatte doch tadellos funktioniert. Ja, zugegeben, das Design war jetzt etwas »reiner« und etwas »sauberer«, aber das Projekt musste Code liefern, der funktioniert, keinen Code, der Akademikern gefällt. Der Berater hingegen schlug vor, weitere zentrale Teile des Systems auf ähnliche Weise aufzuräumen, wodurch das Projekt womöglich ein bis zwei Wochen zum Stillstand käme. Und das Ganze, um den Code schöner zu machen, nicht, damit er etwas kann, was er nicht jetzt schon könnte.

Was halten Sie von dieser Geschichte? Glauben Sie, dass der Berater zu Recht vorschlug, weiter aufzuräumen? Oder neigen Sie eher zur alten Ingenieur-Weisheit »Was nicht kaputt ist, muss man auch nicht reparieren«?

Ich bin hier zugegebenermaßen etwas voreingenommen, denn der Berater war ich. Das Projekt scheiterte sechs Monate später, vor allem, weil der Code zu komplex war, um ihn zu debuggen oder ihn so anzupassen, dass er eine akzeptable Leistung lieferte.

Dann wurde Kent Beck als Berater engagiert, der das Projekt wieder auf die Beine stellen sollte – eine Aufgabe, die es erforderlich machte, fast das gesamte System von Grund auf neu zu schreiben. Er machte einiges anders, aber die wichtigste Änderung war, dass er darauf bestand, den Code durch Refactorings kontinuierlich aufzuräumen. Die erhöhte Effektivität des Teams und die Rolle, die das Refactoring dabei einnahm, inspirierten mich dazu, die erste Ausgabe dieses Buchs zu verfassen – um das von Kent und anderen erworbene Wissen weiterzugeben, das sie sich durch die Anwendung von Refactorings zur Verbesserung der Softwarequalität angeeignet hatten.

Seitdem gehört »Refactoring« in der Programmierung zum gängigen Wortschatz. Tatsächlich hat sich das ursprüngliche Buch auch ziemlich gut behaupten können. Allerdings sind achtzehn Jahre ein hohes Alter für ein Buch über Programmierung. Daher dachte ich mir, es sei an der Zeit, es zu überarbeiten. Dabei habe ich praktisch jede Seite des Buchs neu geschrieben. In gewisser Hinsicht hat sich jedoch nur sehr wenig geändert. Die Essenz des Buches ist unverändert; die meisten der wichtigsten Refactorings sind grundsätzlich die gleichen. Ich hoffe jedoch, dass die Neuformulierungen mehr Lesern dabei helfen, zu erlernen, wie das Refactoring effektiv durchgeführt wird.

Was ist Refactoring?

‌Refactoring ist der Prozess, ein Softwaresystem so zu modifizieren, dass sich das externe Verhalten des Codes nicht ändert, aber dennoch die interne Struktur des Codes zu verbessern. Es handelt sich um eine Vorgehensweise zum Aufräumen von Code, die Disziplin erfordert und die Wahrscheinlichkeit, Bugs zu verursachen, minimiert. Beim Refactoring verbessern Sie im Wesentlichen das Design des Codes, nachdem er geschrieben wurde.

»Verbessern des Designs des Codes, nachdem er geschrieben wurde.« Das ist schon eine seltsame Ausdrucksweise. Seit es Softwareentwicklung gibt, dachten die meisten Leute, dass zunächst das Design entwickelt wird, und dass die Programmierung erst dann erfolgt, wenn es abgeschlossen ist. Der Code wird im Laufe der Zeit modifiziert, und die Integrität des Systems – die dem ursprünglichen Design entsprechende Struktur – geht allmählich verloren. Der Code wird nicht mehr sorgfältig entwickelt, und die Programmierung wird allmählich zum »Hacken«.

Refactoring ist das Gegenteil dieser Vorgehensweise. Mit Refactoring können wir ein schlechtes oder sogar chaotisches Design in sinnvoll strukturierten Code umwandeln. Die einzelnen Schritte sind einfach – geradezu simpel. Ich verschiebe ein Attribut von einer Klasse in eine andere, entnehme einer Methode etwas Code, um daraus eine eigene Methode zu machen oder verschiebe Teile des Codes in der Hierarchie nach oben oder unten. Dennoch kann die Gesamtheit dieser kleinen Änderungen das Design drastisch verbessern. Hierbei handelt es sich um die genaue Umkehrung dessen, was man als Softwarezerfall‌ bezeichnen könnte.

Beim Refactoring verschiebt sich die Gewichtung der Tätigkeiten. Das Design wird nicht nur ganz am Anfang festgelegt, sondern entsteht kontinuierlich während der Entwicklung. Während sich das System entwickelt, finde ich heraus, wie sich das Design verbessern lässt. Diese Interaktion führt zu einem Programm, dessen Design auch bei fortschreitender Entwicklung gut bleibt.

Worum geht es in diesem Buch?

Dieses Buch ist ein Leitfaden für das Refactoring und wendet sich an den fortgeschrittenen Entwickler. Ich möchte Ihnen zeigen, wie Sie Refactorings auf kontrollierte und effiziente Weise durchführen können. Sie erfahren, wie Sie ein Refactoring vornehmen, ohne Bugs im Code zu verursachen, und gleichzeitig die Struktur des Codes systematisch verbessern.

Traditionell beginnt ein Buch mit einer Einführung. Das halte ich im Prinzip für richtig, finde es jedoch schwierig, Refactoring anhand allgemeiner Erklärungen oder Definitionen einzuführen, deshalb folgt zunächst ein Beispiel. In Kapitel 1 wird ein kleines Programm mit einigen typischen Designfehlern durch Refactorings so umstrukturiert, dass es anschließend besser verständlich und leichter modifizierbar ist. Sie lernen dabei den generellen Refactoring-Prozess sowie eine Reihe nützlicher Refactorings kennen. Hierbei handelt es sich um das wichtigste Kapitel, das Sie lesen sollten, wenn Sie verstehen möchten, worum es beim Refactoring eigentlich geht.

In Kapitel 2 erläutere ich weitere allgemeine Prinzipien des Refactorings und stelle einige Definitionen sowie die Gründe für ein Refactoring vor. Anschließend umreiße ich einige der Herausforderungen, die das Refactoring mit sich bringt. In Kapitel 3 unterstützt mich ‌Kent Beck dabei, zu beschreiben, wie man »Code-Smells« identifiziert und wie man sie durch Refactorings bereinigen kann. Auch das Testen spielt beim Refactoring eine sehr wichtige Rolle, deshalb beschreibt Kapitel 4, wie Sie Tests in Ihren Code integrieren können.

Der Hauptgegenstand dieses Buchs – der Refactoring-Katalog – nimmt die verbleibenden Seiten ein. Der Katalog ist zwar alles andere als vollständig, enthält jedoch die wichtigsten Refactorings, die wahrscheinlich von den meisten Entwicklern benötigt werden. Der Katalog ist aus den Notizen entstanden, die ich mir gemacht habe, als ich mich Ende der 1990er Jahre mit Refactoring befasste. Ich verwende diese Notizen auch heute noch, weil ich sie mir nicht alle merken kann. Wenn ich ein Refactoring durchführen möchte, wie etwa ‌Phase aufteilen (Split Phase, Abschnitt 6.11), kann ich im Katalog nachsehen, wie ich das auf sichere Weise Schritt für Schritt tun kann. Ich kann mir nur wünschen, dass Sie diesen Teil des Buchs regelmäßig aufschlagen werden.

Schwerpunkt Internet

Das Internet hat enorme Auswirkungen auf unsere Gesellschaft, insbesondere auf die Art und Weise, wie wir uns Informationen beschaffen. Als ich die erste Ausgabe dieses Buchs verfasste, wurde das Wissen über Softwareentwicklung hauptsächlich durch Druckerzeugnisse vermittelt. Inzwischen beschaffe ich mir die meisten Informationen online. Das stellt für Autoren wie mich eine Herausforderung dar: Haben Bücher noch eine Daseinsberechtigung, und wie sollten sie gestaltet sein?

Ich glaube, Bücher wie dieses haben noch immer ihren Platz – sie müssen sich jedoch ändern. Der Wert eines Buchs besteht in einer großen zusammenhängenden Wissensmenge. Beim Schreiben dieses Buchs habe ich versucht, eine Vielzahl verschiedener Refactorings abzuhandeln und sie in sinnvoller und gegliederter Weise zu organisieren.

Aber im Großen und Ganzen handelt es sich dabei um ein abstraktes literarisches Werk, herkömmlicherweise in Form eines auf Papier gedruckten Buchs, das zukünftig nicht mehr nötig sein wird. Die meisten Verlage bieten noch immer vornehmlich auf Papier gedruckte Bücher an. Zwar wurden eBooks mit Begeisterung angenommen, diese stellen jedoch nur elektronische Versionen der ursprünglichen Werke dar, die auf der Struktur eines auf Papier gedruckten Buchs beruhen.

Mit diesem Buch möchte ich einen anderen Ansatz verfolgen. Die Standardform dieses Buchs ist die dazugehörige englischsprachige Website, die ich als Webbook bezeichne (https://refactoring.com/catalog). Das auf Papier gedruckte Buch stellt eine Auswahl des auf der Website enthaltenen Materials dar, die so zusammengestellt wurde, dass es sinnvoll ist, sie auf Papier zu drucken. Das Buch soll auch gar nicht alle auf der Website vorhandenen Refactorings enthalten, zumal ich zukünftig vermutlich weitere Refactorings zu dem Webbook hinzufügen werde. Auf ähnliche Weise stellt auch das eBook eine bestimmte Auswahl des Webbooks dar.

Während ich das hier schreibe, weiß ich natürlich nicht, ob Sie diese Zeilen auf der Website, in einem eBook, in einem auf Papier gedruckten Buch oder in einer anderen Form lesen, die ich mir noch gar nicht vorstellen kann. Jedenfalls bemühe ich mich, ein nützliches Werk zu erstellen, unabhängig davon, in welcher Form es Ihnen vorliegt.

Codebeispiele in JavaScript

Wie in den meisten technisch orientierten Bereichen der Softwareentwicklung sind Codebeispiele zur Veranschaulichung der Konzepte sehr wichtig. Die Refactorings sind sich in verschiedenen Programmiersprachen allerdings sehr ähnlich. Hin und wieder gibt es gewisse Dinge, die in einer bestimmten Programmiersprache zu beachten sind, die grundlegenden Elemente der Refactorings ändern sich jedoch nicht.

Zur Veranschaulichung der Refactorings habe ich mich für JavaScript‌ entschieden, weil ich den Eindruck habe, dass die meisten Leser diese Programmiersprache verstehen dürften. Es sollte Ihnen aber auch nicht schwerfallen, die Refactorings auf beliebige andere Programmiersprachen zu übertragen. Ich habe mich bemüht, keine der komplizierteren Features der Sprache zu verwenden, damit Sie die Refactorings auch mit nur oberflächlichen JavaScript-Kenntnissen verstehen können. Dass ich JavaScript verwende, ist jedenfalls sicher nicht als uneingeschränkte Befürwortung der Sprache zu verstehen.

Ich verwende für meine Beispiele zwar JavaScript, das heißt jedoch nicht, dass die im Buch vorgestellten Verfahren auf JavaScript beschränkt sind. In der ersten Ausgabe dieses Buchs wurde Java verwendet, und viele Programmierer fanden es nützlich, obwohl sie noch nie zuvor auch nur eine einzige Java-Klasse geschrieben hatten. Ich habe mit dem Gedanken gespielt, diese Allgemeingültigkeit durch die Verwendung von einem Dutzend verschiedener Programmiersprachen für die Beispiele zu verdeutlichen, hatte jedoch den Eindruck, dass dieses Vorgehen für den Leser zu verwirrend wäre. Dessen ungeachtet richtet sich das Buch an Programmierer, die beliebige Sprachen verwenden. Außerhalb der Abschnitte mit Codebeispielen treffe ich keine Annahmen über die verwendete Programmiersprache. Ich gehe davon aus, dass die Leser meine allgemeinen Kommentare zur Kenntnis nehmen und sie auf die von ihnen verwendete Programmiersprache übertragen. Tatsächlich erwarte ich sogar, dass Leser die JavaScript-Beispiele an die von ihnen verwendete Programmiersprache anpassen.

Das bedeutet, dass ich, abseits der Diskussion von bestimmten Beispielen, Begriffe wie »Klasse«, »Modul«, »Funktion« usw. im allgemeinen Kontext der Programmierung verwende und nicht im Kontext des Sprachmodells von JavaScript.

Dass ich für die Codebeispiele JavaScript verwende, bedeutet darüber hinaus auch, dass ich mich bemühe, einen für JavaScript typischen Programmierstil zu vermeiden, der Programmierern, die normalerweise kein JavaScript verwenden, weniger vertraut ist. Es geht in diesem Buch nicht um »Refactoring in JavaScript«, sondern um Refactoring im Allgemeinen, wobei zufälligerweise JavaScript für die Beispiele verwendet wird. Es gibt eine Vielzahl interessanter Refactorings, die spezifisch für JavaScript sind (wie etwa das Refactoring von Callbacks zu Promises oder zu async/await). Diese Refactorings gehen jedoch über den Rahmen dieses Buchs hinaus.

Wer sollte dieses Buch lesen?

Ich richte mich mit diesem Buch an fortgeschrittene Programmierer – an Menschen, die mit dem Schreiben von Software ihren Lebensunterhalt verdienen. Die Beispiele und die Erläuterungen umfassen eine Menge Code, den es zu lesen und zu verstehen gilt. Die Beispiele sind in JavaScript verfasst, sollten aber auf die meisten Programmiersprachen übertragen werden können. Ich erwarte, dass Leser genügend Erfahrung besitzen, um zu verstehen, was in diesem Buch beschrieben wird, umfassende Kenntnisse werden jedoch nicht vorausgesetzt.

Das Buch wendet sich zwar insbesondere an Entwickler, die das Refactoring erlernen möchten, ist aber auch nützlich für jemanden, der bereits Erfahrungen mit dem Refactoring gesammelt hat – es kann also auch als Lernhilfe verwendet werden. Ich habe große Anstrengungen unternommen, um die Funktionsweise der verschiedenen Refactorings zu erklären, damit erfahrene Entwickler dieses Material zur Unterstützung und Beratung ihrer Kollegen nutzen können.

Das Refactoring konzentriert sich zwar vornehmlich auf den Code, hat aber auch erhebliche Auswirkungen auf das Design eines Systems. Für leitende Designer und Softwarearchitekten ist von entscheidender Bedeutung, die Grundlagen des Refactorings zu verstehen und sie in ihren Projekten zu verwenden. Ein Refactoring wird idealerweise durch einen angesehenen und erfahrenen Entwickler eingeleitet. Ein solcher Entwickler kann die dem Refactoring zugrunde liegenden Prinzipien am besten verstehen und sie auf eine bestimmte Situation übertragen. Das trifft insbesondere dann zu, wenn Sie eine andere Sprache als JavaScript verwenden, weil die von mir gezeigten Beispiele an diese andere Programmiersprache angepasst werden müssen.

So können Sie das Buch am besten ausschöpfen, ohne es vollständig zu lesen:

Wenn Sie wissen möchten, was Refactoring eigentlich ist, sollten Sie Kapitel 1 lesen – das Beispiel sollte die Vorgehensweise verdeutlichen.

Wenn Sie wissen möchten, welche Gründe für ein Refactoring sprechen, sollten Sie die ersten beiden Kapitel lesen. Sie erläutern, was Refactoring eigentlich ist und warum es durchgeführt werden sollte.

Wenn Sie wissen möchten, wo ein Refactoring angebracht ist, sollten Sie Kapitel 3 lesen. Darin werden die Anzeichen dafür erläutert, dass ein Refactoring notwendig wäre.

Wenn Sie das Refactoring tatsächlich durchführen möchten, sollten Sie die ersten vier Kapitel vollständig lesen und anschließend den Katalog überfliegen. Lesen Sie so viel, dass Sie in groben Zügen wissen, was der Katalog enthält. Sie brauchen die ganzen Details nicht zu kennen. Lesen Sie ausführlich nach, wenn Sie das Refactoring tatsächlich durchführen, und lassen Sie sich von dem Beispiel unterstützen. Der Katalog ist zum Nachschlagen gedacht, deshalb werden Sie ihn vermutlich nicht in einem Zug durchlesen wollen.

Beim Schreiben des Buchs war die Benennung der verschiedenen Refactorings ein wichtiger Teil. Die Terminologie erleichtert uns die Kommunikation: Wenn ein Entwickler einen anderen darum bittet, einen Codeabschnitt in eine Funktion auszulagern oder eine Berechnung in separate Phasen aufzuteilen, ist beiden klar, was mit den Bezeichnungen ‌Funktion extrahieren (Extract Function, Abschnitt 6.1) und ‌Phase aufteilen (Split Phase, Abschnitt 6.11) gemeint ist. Diese Bezeichnungen sind auch bei der Auswahl automatisierter ‌Refactorings hilfreich.

Von anderen geschaffene Grundlagen

Ich möchte gleich am Anfang klarstellen, dass ich mit diesem Buch eine große Verpflichtung eingegangen bin – eine Verpflichtung denen gegenüber, die in den 1990er Jahren das Fachgebiet Refactoring entwickelt haben. Ich habe aus ihren Erfahrungen gelernt, die es mir ermöglichten und mich dazu inspirierten, die erste Ausgabe dieses Buchs zu verfassen. Auch wenn seitdem viele Jahre vergangen sind, halte ich es für wichtig, die Grundlagen zu würdigen, die hier geschaffen wurden. Idealerweise hätte einer der Schöpfer des Refactorings diese erste Ausgabe verfasst, aber letzten Endes war ich es, der die Zeit und die Kraft fand, diese Aufgabe zu bewältigen.

Ward Cunningham‌ und Kent Beck‌ gehörten zu den führenden ersten Verfechtern des Refactorings. Sie nutzten es schon früh als Grundlage für ihre Projekte und passten ihren Entwicklungsprozess so an, dass sie vom Refactoring profitieren konnten. Insbesondere die Zusammenarbeit mit Kent hat mir immer wieder die große Bedeutung des Refactorings verdeutlicht – eine Erkenntnis, die unmittelbar zum Entstehen dieses Buchs führte.

Ralph Johnson‌ leitet an der University of Illinois in Urbana-Champaign eine Forschungsgruppe, die für ihre praktischen Beiträge zu objektorientierten Technologien bekannt ist. Ralph war lange ein Meister des Refactorings, und einige seiner Studenten haben auf diesem Gebiet unverzichtbare Beiträge geleistet. Bill Opdyke‌ hat mit seiner Doktorarbeit das erste ausführliche schriftliche Werk über Refactoring geschaffen. John Brant‌ und Don Roberts‌ beließen es nicht beim Schreiben von Wörtern – sie schufen das erste automatisierte Werkzeug für Refactoring, den Refactoring-Browser zur Durchführung des Refactorings von Smalltalk-Programmen.

Seit der Veröffentlichung der ersten Ausgabe dieses Buchs haben viele Leute das Fachgebiet Refactoring fortentwickelt. Insbesondere die Arbeit derer, die das automatisierte Refactoring bei der Entwicklung ermöglichten, hat enorm dazu beigetragen, das Leben der Programmierer zu erleichtern. Für mich ist es praktisch selbstverständlich, dass ich eine häufig verwendete Funktion durch eine einfache Tastenkombination umbenennen kann – aber diese Einfachheit beruht auf den Anstrengungen der Programmierer integrierter Entwicklungsumgebungen, deren Arbeit für uns alle von Nutzen ist.

Danksagungen

Auch wenn ich auf all diesen Grundlagen aufbauen konnte, benötigte ich beim Schreiben des Buchs dennoch eine Menge Hilfe. Die erste Ausgabe beruhte in hohem Maß auf der Erfahrung und Unterstützung von Kent Beck. Durch ihn kam ich erstmals mit Refactoring in Berührung. Er inspirierte mich dazu, Notizen zu den Refactorings zu erstellen, und half dabei, sie in lesbare Form zu bringen. Er kam auf die Idee, Refactorings in Form von Gerüchen (»Code-Smells«) zu beschreiben. Ich denke manchmal, er hätte eine bessere Version der ersten Ausgabe geschrieben als ich – wenn er stattdessen nicht damit beschäftigt gewesen wäre, das wegweisende Buch zum Thema Extreme Programming zu verfassen.

Alle mir bekannten Buchautoren weisen darauf hin, wie viel sie ihren technischen Gutachtern verdanken. Wir alle haben Arbeiten mit groben Fehlern verfasst, die nur erkannt wurden, weil unsere Kollegen als Gutachter tätig waren. Ich persönlich beschäftige mich nur wenig mit der Begutachtung technischer Arbeiten, nicht zuletzt, weil ich denke, dass ich das nicht besonders gut kann, deshalb bewundere ich die Kollegen, die sich dieser Aufgabe annehmen. Mit der Begutachtung von Büchern ist noch nicht einmal ein Blumentopf zu gewinnen, deshalb betrachte ich das als einen Akt wahrer Großzügigkeit.

Als ich damit anfing, ernsthaft am Buch zu arbeiten, habe ich eine Mailingliste mit Beratern erstellt, um Feedback zu erhalten. Wenn ich Fortschritte gemacht hatte, stellte ich der Gruppe Entwürfe der neuen Inhalte zur Verfügung und bat um Rückmeldung. Ich möchte mich bei den folgenden Personen für ihr Feedback per Mailingliste bedanken: Arlo Belshee, Avdi Grimm, Beth Anders-Beck, Bill Wake, Brian Guthrie, Brian Marick, Chad Wathington, Dave Farley, David Rice, Don Roberts, Fred George, Giles Alexander, Greg Doench, Hugo Corbucci, Ivan Moore, James Shore, Jay Fields, Jessica Kerr, Joshua Kerievsky, Kevlin Henney, Luciano Ramalho, Marcos Brizeno, Michael Feathers, Patrick Kua, Pete Hodgson, Rebecca Parsons und Trisha Gee.

Aus dieser Gruppe gebührt Beth Anders-Beck, James Shore und Pete Hodgson für ihre Hilfe bezüglich JavaScript besonderer Dank.

Nachdem ich einen mehr oder weniger vollständigen Entwurf fertiggestellt hatte, bat ich weitere Personen um Rückmeldung, weil ich gerne wissen wollte, wie das Buch als Ganzes beim Leser ankommt. William Chargin und Michael Hunger haben mir unglaublich detailliertes Feedback gegeben, und auch Bob Martin und Scott Davis lieferten viele nützliche Kommentare. Bill Wake hat neben seinen Beiträgen zur Mailingliste zudem den ersten kompletten Entwurf vollständig begutachtet.

Meine Kollegen bei ThoughtWorks liefern mir kontinuierlich Ideen und Feedback zu meiner Arbeit. In das Buch sind unzählige Fragen, Kommentare und Anmerkungen eingeflossen. Dass ich als Angestellter von ThoughtWorks einen beträchtlichen Teil meiner Arbeitszeit mit dem Schreiben verbringen kann, ist wirklich toll. Ich schätze insbesondere die regelmäßigen Gespräche mit Rebecca Parsons, unserer technischen Direktorin, und ihre Ideen.

Der Redakteur Greg Doench ist bei Pearson dafür verantwortlich, die vielen bei der Veröffentlichung eines Buchs auftretenden Probleme zu lösen. Julie Nahil ist für die Produktion zuständig. Und es war mir wieder ein Vergnügen, mit Dmitry Kirsanov (Redigieren) und Alina Kirsanov (Zusammenstellung und Verschlagwortung) zusammenzuarbeiten.

Kapitel 1: Refactoring: Ein erstes Beispiel

Wie soll ich anfangen, über das Refactoring zu sprechen? Üblicherweise werden die Geschichte des Themas oder die grundlegenden Prinzipien oder Ähnliches erörtert. Wenn dies auf Konferenzen geschieht, werde ich ein wenig schläfrig. Meine Gedanken schweifen ab, und ein Hintergrundprozess meines Gehirns achtet darauf, ob der Redner ein Beispiel ankündigt, um mich dann zu informieren.

Beispiele dagegen beleben meine Aufmerksamkeit, weil ich nachvollziehen kann, was beschrieben wird. Bei den Prinzipien werden viel zu oft Verallgemeinerungen vorgenommen, sodass es schwierig ist, sich vorzustellen, wie etwas praktisch angewendet wird. Ein Beispiel hingegen ist hilfreich, eine konkrete Anwendung zu verdeutlichen.

Ich werde also dieses Buch mit einem Beispiel für Refactoring beginnen. Anhand dessen werde ich die Funktionsweise des Refactorings erläutern und versuchen, Ihnen ein Gespür für den Refactoring-Prozess zu vermitteln. Im sich daran anschließenden Kapitel werde ich wie üblich mit den Grundlagen fortfahren.

Die Wahl eines einführenden Beispiels stellt mich jedoch vor ein Problem. Wenn ich ein umfangreiches Programm auswähle, es beschreibe und Ihnen erkläre, wie das Refactoring vorzunehmen ist, dann ist das für die meisten zu kompliziert, es zu verstehen. (Ich habe das bei der ersten Ausgabe des vorliegenden Buches ausprobiert – und letztlich zwei Beispiele verwerfen müssen, die zwar ziemlich überschaubar waren, deren Beschreibung aber jeweils mehr als hundert Seiten lang war.) Wähle ich hingegen ein Beispiel aus, das einfach genug ist, um gut nachvollziehbar zu sein, könnte das den Eindruck erwecken, dass sich Refactoring überhaupt nicht lohnt.

Ich bin also mit dem klassischen Problem konfrontiert, mit dem man zu kämpfen hat, wenn man Verfahren beschreiben möchte, die für Programme in der Praxis nützlich sind. Offen gestanden ist es tatsächlich nicht der Mühe wert, alle Refactorings, die ich Ihnen zeigen werde, bei dem kleinen Programm anzuwenden, das ich als Beispiel verwende. Wenn der Code jedoch Teil eines größeren Systems ist, dann wird das Refactoring wichtig. Stellen Sie sich beim Lesen meines Beispiels einfach vor, dass es Bestandteil eines sehr viel größeren Systems ist.

1.2  Anmerkungen zum ersten Programm

Was halten Sie von dem Design dieses Programms? Ich würde zunächst einmal sagen, dass es in dieser Form akzeptabel ist – ein so kurzes Programm benötigt keine tiefgehende Struktur, um verständlich zu sein. Denken Sie jedoch an meinen früheren Hinweis, dass ich kurze Beispiele verwenden muss. Stellen Sie sich dieses Programm deutlich umfangreicher vor – vielleicht mehrere hundert Zeilen lang. Bei einer derartigen Größe ist eine einzelne Funktion schwer zu verstehen.

Ist eine Aussage über die Struktur des Programms in Anbetracht der Tatsache, dass es funktioniert, nicht lediglich ein ästhetisches Urteil? Die Abneigung gegenüber »hässlichem« Code? Dem Compiler ist es schließlich völlig gleichgültig, ob der Code hässlich oder schön ist. Aber wenn ich Änderungen am System vornehme, sind Menschen beteiligt – und Menschen ist es nicht egal, wie der Code aussieht. Es ist schwierig, ein System mit schlechtem Design zu verändern – weil es schwierig ist, herauszufinden, was geändert werden muss und wie diese Änderungen mit dem vorhandenen Code zusammenwirken, um das gewünschte Verhalten zu erzielen. Und wenn es schwierig ist herauszufinden, was geändert werden muss, dann ist die Wahrscheinlichkeit groß, dass mir Fehler unterlaufen und ich Bugs‌ verursache.

Wenn ich Änderungen an einem Programm vornehmen muss, das aus mehreren Hundert Codezeilen besteht, ist es mir daher lieber, wenn es aus einer Reihe von Funktionen und anderen Programmelementen aufgebaut ist, die es mir ermöglichen, die Funktionsweise des Programms leichter zu verstehen. Wenn das Programm keine Struktur aufweist, fällt es mir für gewöhnlich leichter, das Programm zunächst einmal zu strukturieren und erst dann die erforderlichen Änderungen vorzunehmen.

Tipp

Wenn Sie ein Programm um ein bestimmtes Feature erweitern möchten, der Code jedoch nicht in geeigneter Weise strukturiert ist, sollten Sie zunächst ein Refactoring des Programms vornehmen, um es zu vereinfachen, und anschließend das Feature hinzufügen.

In diesem Fall wünschen die Anwender eine Reihe von Änderungen. Zunächst einmal soll die Abrechnung auch in HTML ausgegeben werden.‌ Überlegen Sie sich, welche Auswirkungen diese Änderung haben würde. Ich müsste bei jedem Befehl, der einen String hinzufügt, eine bedingte Anweisung verwenden. In Anbetracht der Tatsache, dass die Komplexität der Funktion dadurch erheblich zunimmt, ziehen es die meisten vor, die Funktion zu kopieren und so zu ändern, dass sie HTML ausgibt. Eine Kopie zu erstellen ist zwar nicht besonders mühsam, kann zukünftig aber für eine Vielzahl an Schwierigkeiten sorgen. Jede Änderung des Abrechnungsverfahrens würde mich dazu zwingen, beide Methoden zu aktualisieren – und zu gewährleisten, dass beide Methoden in übereinstimmender Weise geändert werden. Wenn ich ein Programm schreibe, das sich niemals ändern wird, ist diese Art von Copy & Paste unproblematisch. Schreibe ich hingegen ein langlebiges Programm, kann diese Redundanz gefährlich werden.

Das führt mich zu einer zweiten Änderung. Die Schauspieler möchten weitere Arten von Theaterstücken aufführen. Sie planen, ihrem Repertoire Schäferspiele, Pastoralen, Tragikomödien, Einakter usw. hinzuzufügen. Sie haben noch nicht entschieden, was genau sie eigentlich wollen und wann sie es umsetzen werden. Die Änderungen werden sich jedoch auf das Abrechnungsverfahren und die Berechnung der Treuepunkte auswirken. Im Grunde genommen spielt es keine Rolle, welchen Spielplan sie vorlegen, denn als erfahrener Entwickler kann ich davon ausgehen, dass er in den kommenden sechs Monaten ein weiteres Mal geändert wird. Und wenn neue Features implementiert werden sollen, geht es nicht um einige wenige, sondern um raue Mengen.

Wieder müssen Änderungen an der Klassifikation und dem Abrechnungsverfahren in der Methode statement vorgenommen werden. Wenn ich den Code von statement nach htmlStatement kopiere, muss ich gewährleisten, dass alle Änderungen an den beiden Methoden in übereinstimmender Weise vorgenommen werden. Wenn die Komplexität des Abrechnungsverfahrens zunimmt, wird es darüber hinaus schwieriger, herauszufinden, wo die Änderungen vorgenommen werden müssen, und sie fehlerfrei durchzuführen.

Ich möchte an dieser Stelle betonen, dass es genau diese Änderungen sind, die ein Refactoring erforderlich machen. Wenn der Code funktioniert und nie geändert werden muss, ist es absolut in Ordnung, ihn nicht anzutasten. Es wäre zwar schön, ihn zu verbessern, aber wenn niemand den Code verstehen muss, verursacht er auch keine Probleme. Sobald jedoch jemand die Funktionsweise des Codes verstehen muss und Schwierigkeiten hat, diese nachzuvollziehen, müssen Sie etwas unternehmen.

1.3  Der erste Schritt des Refactorings

Wenn ich ein ‌Refactoring‌ vornehme, ist der erste Schritt stets der gleiche. Ich muss mich vergewissern, dass für den betreffenden Codeabschnitt verlässliche ‌Tests existiere‌n. Die Tests sind unverzichtbar, denn obwohl ich Refactorings auf eine strukturierte Weise durchführe, die fast alle Möglichkeiten dafür ausschließt, dass ich Bugs verursache, bin ich auch nur ein Mensch, und mir unterlaufen Fehler. Je umfangreicher ein Programm ist, desto wahrscheinlicher wird es, dass meine Änderungen versehentlich dazu führen, dass irgendetwas nicht mehr funktioniert. Im digitalen Zeitalter ist Fragilität gleichbedeutend mit Software.

Die Methode statement liefert einen String zurück, daher erstelle ich einige Abrechnungen, indem ich sie mit verschiedenen Arten von Theaterstücken aufrufe und so den String für die Abrechnung erzeuge. Anschließend vergleiche ich die Ergebnisse mit verschiedenen, von Hand erzeugten Referenzstrings. Zur Einrichtung dieser Tests verwende ich ein Test-Framework, sodass ich sie durch einen einfachen Tastendruck in meiner Entwicklungsumgebung ausführen kann. Die Ausführung der Tests dauert nur ein paar Sekunden, und wie Sie sehen werden, führe ich sie häufig aus.

Ein wichtiger Bestandteil der Tests ist die Art und Weise, wie die Ergebnisse dargestellt werden. Sie sind entweder grün, was bedeutet, dass alle Strings mit den Referenzstrings übereinstimmen, oder rot, in Form einer Liste von aufgetretenen Fehlern – die Zeilen, die ein abweichendes Ergebnis enthalten. Die Tests sind also ‌selbsttestend‌, und das ist unverzichtbar. Anderenfalls müsste ich die Testergebnisse am Schreibtisch von Hand mit den Referenzwerten vergleichen, und das würde mich stark verlangsamen. Aktuelle Test-Frameworks verfügen über alle notwendigen Features, um selbsttestende Tests zu schreiben.

Tipp

Vergewissern Sie sich, dass Ihnen verlässliche Tests zur Verfügung stehen, bevor Sie das Refactoring durchführen. Diese Tests müssen selbsttestend sein.

Bei der Durchführung des Refactorings verlasse ich mich auf die Tests. Ich stelle sie mir als Bug-Detektoren vor, die mich vor meinen eigenen Fehlern schützen. Um mein Ziel zu erreichen, muss ich es zweimal formulieren, sowohl im Code als auch im Test. Ich müsste einen Fehler an beiden Stellen und auf übereinstimmende Weise begehen, um den Detektor zu umgehen. Durch diese doppelte Überprüfung verringere ich die Wahrscheinlichkeit, etwas falsch zu machen. Es kostet zwar etwas Zeit, die Tests zu erstellen, das macht die beim Debugging eingesparte Zeit jedoch mehr als wett. Dieser Teil ist bei der Durchführung eines Refactorings von so großer Bedeutung, dass ich ihm ein eigenes Kapitel gewidmet habe (Kapitel 4, Tests erstellen).