Zum Inhalt springen
L

Wikipedia · einfach zusammengefasst · Stand

Refactoring

Refactoring ist ein zentraler Bestandteil der Agilen Softwareentwicklung. Dort wird meist von „kontinuierlichem“ Refactoring oder „kompromisslosem“ Refactoring …

Inhalt6 Abschnitte
  1. 1. Grundidee
  2. 2. Ziele und Vorgehen
  3. 3. Typische Maßnahmen
  4. 4. Nutzen und Grenzen
  5. 5. Risiken beherrschen
  6. 6. Einfaches Beispiel

Grundidee

Refactoring, auch Refaktorisierung, Refaktorierung oder Restrukturierung genannt, bezeichnet in der Software-Entwicklung die manuelle oder automatisierte Verbesserung der Struktur von Quelltext, ohne das beobachtbare Verhalten des Programms zu verändern. Ein Programm soll also nach außen dasselbe tun wie vorher, aber der Code soll leichter zu lesen, zu verstehen, zu warten und zu erweitern sein. Dadurch soll der Aufwand für Fehleranalyse und spätere funktionale Erweiterungen deutlich sinken.

In der Agilen Softwareentwicklung ist Refactoring ein zentraler Bestandteil des Entwicklungsprozesses. Dort spricht man oft von „kontinuierlichem“ oder „kompromisslosem“ Refactoring. Es findet nicht nur in besonderen Phasen statt, sondern gehört wie Programmieren oder Modultesten dauerhaft zur täglichen Arbeit an Software.

Ziele und Vorgehen

Refactoring wird vor allem auf unschöne Stellen im Code angewandt, die als Code-Smells bezeichnet werden. Ein Code-Smell ist ein Hinweis darauf, dass Quelltext möglicherweise schlecht strukturiert ist, auch wenn er noch funktioniert. Beim Refactoring wird der Code umgestaltet, wobei die tatsächliche Programmfunktion gleich bleiben soll.

Wichtige Ziele sind Lesbarkeit, Übersichtlichkeit, Verständlichkeit, Erweiterbarkeit, Vermeidung von Redundanz und Testbarkeit. Redundanz bedeutet, dass gleiche oder sehr ähnliche Logik mehrfach im Code vorkommt. Testbarkeit bedeutet, dass sich das Verhalten des Codes gut mit Tests prüfen lässt.

Unterstützt wird Refactoring durch Unit-Tests, Werkzeuge in integrierten Entwicklungsumgebungen, funktionale Programmiersprachen und Programmiersprachen mit strengem Typsystem. Unit-Tests können als Regressionstests zeigen, ob sich das Verhalten unter gleichen Bedingungen nicht verändert hat. Ein strenges Typsystem kann schon zur Compile-Time viele Fehler ausschließen, weil es darauf achtet, dass die Signatur, also das Interface, gleich bleibt, auch wenn sich die Implementierung ändert.

Typische Maßnahmen

Häufige Refactorings sind das Umbenennen von Symbolen, etwa sprechende Namen für Variablen, Konstanten oder Methoden, sowie das Verschieben eines Symbols in ein anderes Modul, zum Beispiel einer Methode in eine andere Klasse.

Weitere Maßnahmen sind das Aufteilen eines Moduls wie Paket, Klasse oder Methode in mehrere kleinere Module oder das Zusammenlegen kleinerer Module zu einem größeren. Auch die Umformatierung von Quelltext, etwa mit einem Beautifier, kann im weiteren Sinn dazugehören.

In funktionalen Programmiersprachen können Funktionen höherer Ordnung eingesetzt oder gemeinsame abstrakte Logik mehrerer Module in Funktoren ausgelagert werden. Funktoren sind parametrisierte Module, die Module als Parameter erhalten und Module als Ergebnis liefern. Außerdem können zwei oder mehrere generisch sehr ähnliche Funktionalitäten zu einer allgemeineren Funktionalität zusammengefasst werden. Auch das Beseitigen von totem Code gehört zu den typischen Arbeiten. Toter Code ist Code, der nicht mehr verwendet wird.

Nutzen und Grenzen

Der wichtigste Vorteil von Refactoring ist eine bessere Wartbarkeit des Designs. Für Programmierer wird es leichter, bestehenden Code funktional zu erweitern oder an anderer Stelle wiederzuverwenden. Dazu wird der Code besonders hinsichtlich Lesbarkeit, Modularität, Redundanz, Kopplung, Kohäsion und Testbarkeit verbessert.

Modularität bedeutet, dass ein Programm in sinnvoll getrennte Bausteine gegliedert ist. Kopplung beschreibt, wie stark Teile eines Programms voneinander abhängen. Kohäsion beschreibt, wie gut die Bestandteile eines Moduls inhaltlich zusammengehören. Gute Kopplung und Kohäsion sollen bewirken, dass spätere Änderungen möglichst nur lokale Auswirkungen haben.

Refactoring erlaubt es, den üblichen Softwareentwicklungszyklus aus Spezifikation, Design, Implementierung und Tests im Kleinen ständig weiterzuführen. Dadurch kann ein Produkt kontinuierlich verbessert werden, statt nach Änderungen der Spezifikation oder nach einem Redesign große Teile oder sogar das ganze Programm neu schreiben zu müssen.

Es gibt aber auch Nachteile. Wie bei jeder Codeänderung können durch Refactoring neue, unerwartete Fehler entstehen. Wenn Regressionstests nicht automatisiert sind, entsteht zusätzlicher Testaufwand. Außerdem kann Refactoring Zeit verbrauchen, ohne echten Nutzen für den Kunden zu bringen, wenn es in Richtung spezieller Designausprägungen geht, die nicht der Wiederverwendung dienen und von wichtigeren Aufgaben ablenken.

Risiken beherrschen

Refactoring wird nur auf funktionierendem Code ausgeführt, dessen Funktionalität erhalten bleiben soll. Genau darin liegt ein Risiko: Beim Umbau können ungewollte Änderungen und Fehler entstehen. Deshalb werden Regeln verwendet, die den Prozess weniger gefährlich machen.

Zuerst sollte es eine Reihe automatisch ablaufender Unit-Tests geben. Diese werden vor dem Refactoring ausgeführt, und man beginnt erst, wenn alle Tests funktionieren. Zusätzlich sollte die Testabdeckung geprüft werden, also ob die zu ändernde Stelle wirklich durch automatisierte Tests geschützt ist. Nach dem Refactoring wird die Testsuite erneut ausgeführt. Unit-Tests machen Refactoring jedoch nicht völlig sicher, sondern senken nur das Risiko.

Ein weiteres Prinzip sind kleine Änderungen. Wenn nur wenig verändert wird, lassen sich Fehler meist leichter finden. Komplexe Refactorings können oft in kleine einfache Einheiten zerlegt werden. Vor und nach jedem Schritt wird die Integrität des Systems durch Tests geprüft. Automatisierte Refactoring-Funktionen, etwa in Eclipse oder Borland Delphi ab Version 2005, können Fehlerquellen verringern und Arbeitsaufwand sparen.

Zusätzlich gibt es Refactoring-Muster, ähnlich wie Entwurfsmuster. Ein Muster beschreibt ein Ziel, zum Beispiel „Methode extrahieren“ oder „Klasse umbenennen“, und dazu passende Arbeitsanweisungen. Viele Muster können heute automatisch von Werkzeugen umgesetzt werden. Trotzdem können diese Mechanismen fehleranfällig sein; im besten Fall entstehen Übersetzungsfehler, im schlechteren Fall Laufzeitfehler. Deshalb ist umfangreiches, möglichst automatisiertes Testen nach einem Refactoring immer erforderlich.

Einfaches Beispiel

Das Beispiel zeigt Java-Code, in dem eine temporäre Variable mit dem nichtssagenden Namen x für zwei verschiedene Zwecke verwendet wird: zuerst für den Umfang eines Rechtecks und danach für die Fläche. Durch Refactoring werden daraus zwei getrennte Variablen mit aussagekräftigen Namen: umfang und flaeche. Dadurch wird klarer, welche Bedeutung die jeweiligen Berechnungen haben.

Ein weiteres Refactoring kann die lokalen Variablen entfernen und die Ausdrücke direkt in die Ausgabe schreiben. Das hat aber Nachteile: Die Bedeutung der Ausdrücke wird unklarer, und die Ausdrücke können schlechter im Debugger angezeigt werden. Laut Artikel wird der entstehende Code dadurch weder besser noch schlechter, da Compiler seit Mitte der 1990er Jahre Common subexpression elimination und Live variable analysis beherrschen.

Als weitere Möglichkeit kann die Berechnung in eine Klasse Rechteck verlegt werden. Dann werden Methoden wie umfang(), flaeche(), ecken() oder diagonale(0,1) verwendet. Das Beispiel zeigt, dass Refactoring nicht nur Namen ändern kann, sondern auch Verantwortlichkeiten im Code anders verteilen kann.

Lernvideos zu Refactoring

Weiterlesen

Quelltext Quelltext, auch Quellcode (englisch source code) oder unscharf Programmcode genannt, ist in der Informatik der für Menschen lesbare, in einer … Fehler Ein Fehler ist die Abweichung eines Zustands, Vorgangs oder Ergebnisses von einem Standard, den Regeln oder einem Ziel. Agile Softwareentwicklung Agile Softwareentwicklung zeichnet sich durch selbstorganisierende Teams sowie eine iterative und inkrementelle Vorgehensweise aus. Agile Ansätze können sich … Faktorisierung von Polynomen Als Faktorisierung von Polynomen in der Algebra versteht man analog zur Primfaktorzerlegung von ganzen Zahlen das Zerlegen von Polynomen in ein Produkt aus … Mathematik An deutschen Universitäten gehört die Mathematik meistens zur selben Fakultät wie die Naturwissenschaften, und so wird Mathematikern nach der Promotion in der … Code-Smell Unter dem Begriff sollten handfestere Kriterien für Refactoring beschrieben werden, als das durch den vagen Hinweis auf Programmästhetik geschehen würde. Bei … Term In der Mathematik ist ein Term eine sinnvolle Kombination aus Zahlen, Variablen, Symbolen für mathematische Verknüpfungen und Klammern. Integrierte Entwicklungsumgebung Eine integrierte Entwicklungsumgebung (IDE, von englisch integrated development environment) ist eine Sammlung von Computerprogrammen, mit denen die … Softwaredesign Softwaredesign (auch Softwarekonstruktion) ist der Konstruktionsprozess zur Implementierung einer Software-Lösung. Üblicherweise vollzieht sich die … Redundanz (Informationstheorie) Eine Informationseinheit ist dann redundant, wenn sie ohne Informationsverlust weggelassen werden kann. Das Identifizieren und Entfernen solcher Redundanzen … Kopplung (Softwareentwicklung) Unter Kopplung versteht man in der Informatik die Verknüpfung von verschiedenen Systemen, Anwendungen oder Softwaremodulen sowie ein Maß, das die Stärke … Softwaretest Ein Softwaretest prüft und bewertet Software auf Erfüllung der für ihren Einsatz definierten Anforderungen und misst ihre Qualität.