Refactoring. Dominique Steiner 1 und Monica De Donato 2. dedom1@bfh.ch

Ähnliche Dokumente
Softwaretests in Visual Studio 2010 Ultimate Vergleich mit Java-Testwerkzeugen. Alexander Schunk Marcel Teuber Henry Trobisch

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Diese Ansicht erhalten Sie nach der erfolgreichen Anmeldung bei Wordpress.

Dieser Ablauf soll eine Hilfe für die tägliche Arbeit mit der SMS Bestätigung im Millennium darstellen.

Fachbericht zum Thema: Anforderungen an ein Datenbanksystem

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Software Engineering Klassendiagramme Assoziationen

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Hilfe zur Urlaubsplanung und Zeiterfassung

SEP 114. Design by Contract

Klassenentwurf. Wie schreiben wir Klassen, die leicht zu verstehen, wartbar und wiederverwendbar sind? Objektorientierte Programmierung mit Java

Suche schlecht beschriftete Bilder mit Eigenen Abfragen

Java: Vererbung. Teil 3: super()

Bilder zum Upload verkleinern

ARCO Software - Anleitung zur Umstellung der MWSt

bilder.tibs.at Upload-Assistent

! " # $ " % & Nicki Wruck worldwidewruck

Urlaubsregel in David

Anwendungsbeispiele. Neuerungen in den s. Webling ist ein Produkt der Firma:

Abamsoft Finos im Zusammenspiel mit shop to date von DATA BECKER

Mit der Maus im Menü links auf den Menüpunkt 'Seiten' gehen und auf 'Erstellen klicken.

Anleitung zur Daten zur Datensicherung und Datenrücksicherung. Datensicherung

Zimmertypen. Zimmertypen anlegen

Menü Macro. WinIBW2-Macros unter Windows7? Macros aufnehmen

Windows. Workshop Internet-Explorer: Arbeiten mit Favoriten, Teil 1

1. Software installieren 2. Software starten. Hilfe zum Arbeiten mit der DÖHNERT FOTOBUCH Software

Informatik 2 Labor 2 Programmieren in MATLAB Georg Richter

Er musste so eingerichtet werden, dass das D-Laufwerk auf das E-Laufwerk gespiegelt

Zwischenablage (Bilder, Texte,...)

Kostenstellen verwalten. Tipps & Tricks

Programmierkurs Java

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

teischl.com Software Design & Services e.u. office@teischl.com

Kleines Handbuch zur Fotogalerie der Pixel AG

FastBill Automatic. Dokumentation Versand. FastBill GmbH. Holteyer Straße Essen Telefon Telefax

EINFACHES HAUSHALT- KASSABUCH

Computeria Rorschach Mit Excel Diagramme erstellen

1. Einschränkung für Mac-User ohne Office Dokumente hochladen, teilen und bearbeiten

Javadoc. Programmiermethodik. Eva Zangerle Universität Innsbruck

WordPress. Dokumentation

Problemdefinition. Cash Flow Reminder. 1 Problembeschreibung: 2 Projektziel: 3 Aufgaben des Benutzers

Beispiel Shop-Eintrag Ladenlokal & Online-Shop im Verzeichnis 1

Der Kalender im ipad

Professionelle Seminare im Bereich MS-Office

Arbeiten mit UMLed und Delphi

etermin Einbindung in Outlook

Objektorientierte Programmierung

Programmieren in Java

Firefox: Die Lesezeichen im Griff

Die Dateiablage Der Weg zur Dateiablage

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

Tipps & Tricks im CRM

Prozessbewertung und -verbesserung nach ITIL im Kontext des betrieblichen Informationsmanagements. von Stephanie Wilke am

Drei-Schichten-Architektur. Informatik B - Objektorientierte Programmierung in Java. Vorlesung 16: 3-Schichten-Architektur 1 Fachkonzept - GUI

Ich möchte eine Bildergalerie ins Internet stellen

Unsere Webapplikation erweitern

Refactoring. PG Reclipse Seminar: Refactoring Jan-Christopher Bals (1/30)

Das Design: Themen und Varianten anwenden

Second Steps in eport 2.0 So ordern Sie Credits und Berichte

Wichtige Hinweise zu den neuen Orientierungshilfen der Architekten-/Objektplanerverträge

Internet Explorer Version 6

Reporting Services und SharePoint 2010 Teil 1

Sie wollen Was heißt das? Grundvoraussetzung ist ein Bild oder mehrere Bilder vom Wechseldatenträger

Fachdidaktik der Informatik Jörg Depner, Kathrin Gaißer

OP-LOG

Das Persönliche Budget in verständlicher Sprache

SICHERN DER FAVORITEN

1. Einführung. 2. Weitere Konten anlegen

Installation von Updates

Online Newsletter III

ACDSee 2009 Tutorials: Rote-Augen-Korrektur

Hilfe zur Dokumentenverwaltung

Adobe Photoshop. Lightroom 5 für Einsteiger Bilder verwalten und entwickeln. Sam Jost

Einführung in die Informatik Tools

Kurzanleitung MAN E-Learning (WBT)

Fachhochschule Deggendorf Platzziffer:...

Die Beschreibung bezieht sich auf die Version Dreamweaver 4.0. In der Version MX ist die Sitedefinition leicht geändert worden.

SAMMEL DEINE IDENTITÄTEN::: NINA FRANK :: :: WINTERSEMESTER 08 09

Fülle das erste Bild "Erforderliche Information für das Google-Konto" vollständig aus und auch das nachfolgende Bild.

Datensicherung. Beschreibung der Datensicherung

YouTube: Video-Untertitel übersetzen

Software Engineering Interaktionsdiagramme

Kapiteltests zum Leitprogramm Binäre Suchbäume

Kurzanleitung. MEYTON Aufbau einer Internetverbindung. 1 Von 11

Word austricksen FORTSETZUNG. Serienbriefe Für Word XP. Word austricksen Fortsetzung: Serienbriefe. Roland Egli Educational Solutions

Einrichtung eines -Zugangs mit Mozilla Thunderbird

Tevalo Handbuch v 1.1 vom

Gruppenrichtlinien und Softwareverteilung

Kapitel 3 Frames Seite 1

Anmeldung bei einem registrierten Konto (Account)

Datenbank-Verschlüsselung mit DbDefence und Webanwendungen.

Bedienungsanleitung. Stand: Copyright 2011 by GEVITAS GmbH

MORE Profile. Pass- und Lizenzverwaltungssystem. Stand: MORE Projects GmbH

Comic Life 2.x. Fortbildung zum Mediencurriculum

Artikel Schnittstelle über CSV

Anwendungspraktikum aus JAVA Programmierung im SS 2006 Leitung: Albert Weichselbraun. Java Projekt. Schiffe Versenken mit GUI

Updatehinweise für die Version forma 5.5.5

Das Leitbild vom Verein WIR

Nicht über uns ohne uns

Transkript:

Refactoring Dominique Steiner 1 und Monica De Donato 2 1 Berner Fachhochschule Technik und Informatik, 2502 Biel, CH, steid3@bfh.ch, 2 Berner Fachhochschule Technik und Informatik, 2502 Biel, CH, dedom1@bfh.ch Abstract. Refaktorisieren 3 ist eine Technik, die benutzt wird, um die Struktur bestehender Software, die im Laufe der Zeit an Qualität verloren hat, zu überarbeiten, ohne dabei die Funktionalitäten zu verändern. Mit Refaktorisieren wird nicht, wie vermutet Zeit verloren, sondern durch den übersichtlicheren Code kann beim Implementieren von neuen Funktionalitäten sogar Zeit gewonnen werden. Das Java-Beispiel bietet einen kleinen Einblick in die Technik und das Vorgehen beim Refaktorisieren. Um Programmierern das Refaktorisieren zu vereinfachen, bieten viele moderne Entwicklungsumgebungen Möglichkeiten an, die diesen Prozess unterstützen und automatisieren. Selbstprüfende Tests sind beim Rektorisieren zum Überprüfen des Codes und zum Vermeiden von Fehlern unumgänglich. Beim Verwenden von Java ist JUnit ein sehr praktisches Hilfsmittel. Neben all den positiven Aspekten gibt es Situationen, in denen der Einsatz von Refaktorisierung zusätzliche Schwierigkeiten mit sich bringt, oder in denen ganz darauf verzichtet werden sollte. Key words: Refaktorisieren, Lesbarkeit, Übersichtlichkeit, Tests, Struktur Optimierung 1 Was ist Refaktorisieren? Im Herstellungsprozess von Software werden immer wieder, von verschiedenen Programmierern, Änderungen und neue Funktionen in die Programme eingebaut. Dies führt dazu, dass die Software immer unübersichtlicher wird und die Struktur der Software darunter leidet. Mit der Zeit wird es sehr auwändig Änderungen vorzunehmen, weil sich diese in der aktuellen Struktur fast nicht einbauen lassen. Um dieses Phänomen zu minimieren, kann der Code regelmässig refaktorisiert werden. Refaktorisieren ist eine Technik, bei der in kleinen Schritten die Struktur und die Verständlichkeit bestehender Software, durch anwenden bestimmter Refaktorisierungen, verbessert wird. Dazu gehören zum Beispiel Methode extrahieren, Variable und Methode umbenennen, Klasse extrahieren und viele weitere. Anschliessend lassen sich in der optimierten Softwareänderungen und neue Funktionalitäten viel einfacher einbauen. Im Buch von Martin Fowler 3 Der Einfachheit halber wird die deutsche Form verwendet, obwohl diese nicht ganz der deutschen Rechtschreibung entspricht.

2 Dominique Steiner, Monica De Donato ndet man die folgenden Denitionen, je nach Kontext in dem das Wort verwendet wird. Denition 1. Refaktorisierung (Substantiv): Eine Änderung an der internen Struktur einer Software, um sie leichter verständlich zu machen und einfacher zu verändern, ohne ihr beobachtetes Verhalten zu ändern. [1] Denition 2. Refaktorisieren (Verb): Eine Software umstrukturieren, ohne ihr beobachtetes Verhalten zu ändern, indem man eine Reihe von Refaktorisierungen anwendet. [1] Wichtig ist, dass während dem Refaktorisieren die Funktionalitäten des Codes nicht verändert werden. Die Software muss nach dem Refaktorisieren genau das Gleiche tun wie vorher. Um dies zu erreichen, müssen die folgenden Schritte eingehalten werden. Zuerst müssen automatisierte Tests geschrieben werden, die alle bisherigen Funktionalitäten der Software testen und die während dem Refaktorisieren immer wieder angewendet werden können. Diese Tests geben die Sicherheit, dass keine Fehler eingebaut werden und dass sich das Verhalten der Software nicht verändert. Im zweiten Schritt wird der Code durch das Anwenden von Refaktorisierungen neu strukturiert und vereinfacht. Erst wenn die Phase des Refaktorisierens abgeschlossen ist, dürfen der Software wieder neue Funktionalitäten hinzugefügt werden. Das folgende Java-Beispiel, ein bearbeiteter Ausschnitt des Beispiels von Fowler [1], zeigt exemplarisch das Anwenden einfacher Refaktorisierungen. Es handelt sich um Software eines Videoverleihs, die die Rechnungen der Kunden druckt. Dazu wird anhand der Ausleihdauer und der Kategorie von Filmen die Miete berechnet. Zusätzlich werden bei Filmen, die der Kategorie 'Neuerscheinungen' angehören, Bonuspunkte vergeben, die auch auf der Rechnung vermerkt werden. Fig. 1. Assoziationen zwischen den Klassen Movie, Rental und Customer // Datenklasse fuer die verschiedenen Filme public class Movie { public static final int CHILDREN = 2; public static final int REGULAR = 0; public static final int NEW_ RELEASE = 1; private String title ; private int pricecode ;

Refactoring 3 public Movie ( String title, int pricecode ){ this. title = title ; this. pricecode = pricecode ; public int getpricecode () { return this. pricecode ; public void setpricecode ( int arg ){ this. pricecode = arg ; public String gettitle () { return this. title ; // Die Klasse Rental repraeseniert die Ausleihen eines Films durch einen Kunden class Rental { private Movie movie ; private int daysrented ; public Rental ( Movie movie, int daysrented ){ this. movie = movie ; this. daysrented = daysrented ; public int getdaysrented () { return this. daysrented ; public Movie getmovie () { return this. movie ; // Klasse fuer Kunden der Videothek public class Customer { private String name ; private List < Rental > rentals = new ArrayList < Rental >() ; public Customer ( String name ){ this. name = name ; public void addrental ( Rental arg ){ this. rentals. add ( arg ); public String getname () { return this. name ; public String statement () { double totalamount = 0;

4 Dominique Steiner, Monica De Donato int frequentrenterpoints = 0; Iterator it = rentals. iterator () ; String result = " Rental Record for " + getname () + "\ n"; while ( it. hasnext () ) { double thisamount = 0; Rental each = ( Rental ) it. next () ; // Betraege pro Zeile ermitteln switch ( each. getmovie (). getpricecode () ){ case Movie. REGULAR : thisamount +=2; if ( each. getdaysrented () > 2) thisamount += ( each. getdaysrented () - 2) * 1.5; case Movie. NEW_ RELEASE : thisamount += each. getdaysrented () * 3; case Movie. CHILDREN : thisamount += 1.5; if ( each. getdaysrented () > 3) thisamount += ( each. getdaysrented () - 3) * 1.5; // Bonuspunkte aufaddieren frequentrenterpoints ++; // Bonuspunkte fuer zweitaegige Ausleihe einer Neuerscheinung if (( each. getmovie (). getpricecode () == Movie. NEW_RELEASE ) && each. getdaysrented () > 1) frequentrenterpoints ++; // Zahlen fuer diese Ausleihe ausgeben result += "\t" + each. getmovie (). gettitle () + "\t" + String. valueof ( thisamount ) + "\n"; totalamount += thisamount ; // Fusszeilen einfuegen result += " Amount owned is " + String. valueof ( totalamount ) + "\n"; result += " You earned " + String. valueof ( frequentrenterpoints ) + " frequent renter points "; return result ; Die Methode statement() gibt einen String mit der Rechnung des Kunden zurück. Ziel des Beispiels von Fowler ist es, die gewünschte Änderung, die Ausgabe der

Refactoring 5 Rechnung in HTML, zu ermöglichen. Dies wäre hier zwar möglich, es müsste dazu jedoch eine ganz neue Methoden geschrieben werden, da die bisherige Methode statement() sehr lang und unübersichtlich ist. Zudem lassen sich auch keine Teile dieser Methode wiederverwenden, was dazu führen würde, dass nach dem Einfügen der Änderung duplizierter Code vorhanden ist. Dies soll nun mit Hilfe von Refaktorisieren geändert werden, damit einzelne Funktionen, die sich bisher in der Methode statement() benden, auch für die moderne Ausgabe zur Verfügung stehen. Dazu wird im ersten Schritt der Switch-Abschnitt mit Methode extrahieren in eine eigenständige Methode amountfor() umgewandelt. Dabei muss besonders auf die Parameter und lokalen Variablen acht gegeben werden. private String name ; private List < Rental > rentals = new ArrayList < Rental >() ; public Customer ( String name ){ this. name = name ; public void addrental ( Rental arg ){ this. rentals. add ( arg ); public String getname () { return this. name ; public String statement () { double totalamount = 0; int frequentrenterpoints = 0; Iterator it = rentals. iterator () ; String result = " Rental Record for " + getname () + "\ n"; while ( it. hasnext () ) { double thisamount = 0; Rental each = ( Rental ) it. next () ; thisamount = amountfor ( each ); // Bonuspunkte aufaddieren frequentrenterpoints ++; // Bonuspunkte fuer zweitaegige Ausleihe einer Neuerscheinung if (( each. getmovie (). getpricecode () == Movie. NEW_RELEASE ) && each. getdaysrented () > 1) frequentrenterpoints ++; // Zahlen fuer diese Ausleihe ausgeben result += "\t" + each. getmovie (). gettitle () + "\t" + String. valueof ( thisamount ) + "\n"; totalamount += thisamount ;

6 Dominique Steiner, Monica De Donato // Fusszeilen einfuegen result += " Amount owned is " + String. valueof ( totalamount ) + "\n"; result += " You earned " + String. valueof ( frequentrenterpoints ) + " frequent renter points "; return result ; private double amountfor ( Rental each ) { double thisamount = 0; // Betraege pro Zeile ermitteln switch ( each. getmovie (). getpricecode () ){ case Movie. REGULAR : thisamount +=2; if ( each. getdaysrented () > 2) thisamount += ( each. getdaysrented () - 2) * 1.5; case Movie. NEW_ RELEASE : thisamount += each. getdaysrented () * 3; case Movie. CHILDREN : thisamount += 1.5; if ( each. getdaysrented () > 3) thisamount += ( each. getdaysrented () - 3) * 1.5; return thisamount ; Als zweiter Schritt werden nun die Variablen each und thisamount umbenennt. Durch einen aussagekräftigen Namen wird der Code besser lesbar und es ist sofort erkennbar, was für ein Wert in der Variable gespeichert ist. private double amountfor ( Rental arental ) { double result = 0; // Betraege pro Zeile ermitteln switch ( arental. getmovie (). getpricecode () ){ case Movie. REGULAR : result +=2; if ( arental. getdaysrented () > 2) result += ( arental. getdaysrented () - 2) * 1.5; case Movie. NEW_ RELEASE : result += arental. getdaysrented () * 3; case Movie. CHILDREN : result += 1.5;

Refactoring 7 if ( arental. getdaysrented () > 3) result += ( arental. getdaysrented () - 3) * 1.5; return result ; Da die Methode amountfor() enger mit der Klasse Rental als mit der Klasse Customer zusammenarbeitet, ist es sinnvoller, diese Methode in die Klasse Rental zu verschieben. Beim Verschieben der Methode, muss diese der neuen Umgebung angepasst werden. class Rental { private Movie movie ; private int daysrented ; public Rental ( Movie movie, int daysrented ){ this. movie = movie ; this. daysrented = daysrented ; public int getdaysrented () { return this. daysrented ; public Movie getmovie () { return this. movie ; double getcharge () { double result = 0; // Betraege pro Zeile ermitteln switch ( getmovie (). getpricecode () ){ case Movie. REGULAR : result +=2; if ( getdaysrented () > 2) result += ( getdaysrented () - 2) * 1.5; case Movie. NEW_ RELEASE : result += getdaysrented () * 3; case Movie. CHILDREN : result += 1.5; if ( getdaysrented () > 3) result += ( getdaysrented () - 3) * 1.5; return result ; public class Customer {

8 Dominique Steiner, Monica De Donato... /* * * @ deprecated Use { @link Rental # getcharge () instead */ private double amountfor ( Rental arental ) { return arental. getcharge () ; Nun wird der Methodenaufruf in der Klasse Customer durch einen Aufruf der neuen Klasse ersetzt. Dadurch kann die veraltete Methode aus der Klasse Customer entfernt werden. public class Customer { private String name ; private List < Rental > rentals = new ArrayList < Rental >() ; public Customer ( String name ){ this. name = name ; public void addrental ( Rental arg ){ this. rentals. add ( arg ); public String getname () { return this. name ; public String statement () { double totalamount = 0; int frequentrenterpoints = 0; Iterator it = rentals. iterator () ; String result = " Rental Record for " + getname () + "\ n"; while ( it. hasnext () ) { double thisamount = 0; Rental each = ( Rental ) it. next () ; thisamount = each. getcharge () ; // Bonuspunkte aufaddieren frequentrenterpoints ++; // Bonuspunkte fuer zweitaegige Ausleihe einer Neuerscheinung if (( each. getmovie (). getpricecode () == Movie. NEW_RELEASE ) && each. getdaysrented () > 1) frequentrenterpoints ++; // Zahlen fuer diese Ausleihe ausgeben

Refactoring 9 result += "\t" + each. getmovie (). gettitle () + "\t" + String. valueof ( thisamount ) + "\n"; totalamount += thisamount ; // Fusszeilen einfuegen result += " Amount owned is " + String. valueof ( totalamount ) + "\n"; result += " You earned " + String. valueof ( frequentrenterpoints ) + " frequent renter points "; return result ; Da thisamount in dieser Klasse nur zweimal vorkommt, kann diese Variable durch den Einsatz von direkten Methodenaufrufen eingespart werden. public class Customer { private String name ; private List < Rental > rentals = new ArrayList < Rental >() ; public Customer ( String name ){ this. name = name ; public void addrental ( Rental arg ){ this. rentals. add ( arg ); public String getname () { return this. name ; public String statement () { double totalamount = 0; int frequentrenterpoints = 0; Iterator it = rentals. iterator () ; String result = " Rental Record for " + getname () + "\ n"; while ( it. hasnext () ) { Rental each = ( Rental ) it. next () ; // Bonuspunkte aufaddieren frequentrenterpoints ++; // Bonuspunkte fuer zweitaegige Ausleihe einer Neuerscheinung if (( each. getmovie (). getpricecode () == Movie. NEW_RELEASE ) && each. getdaysrented () > 1) frequentrenterpoints ++; // Zahlen fuer diese Ausleihe ausgeben result += "\t" + each. getmovie (). gettitle () + "\t" + String. valueof ( each. getcharge () ) + "\n";

10 Dominique Steiner, Monica De Donato totalamount += each. getcharge () ; // Fusszeilen einfuegen result += " Amount owned is " + String. valueof ( totalamount ) + "\n"; result += " You earned " + String. valueof ( frequentrenterpoints ) + " frequent renter points "; return result ; Nun ist die Methode statement() der Klasse Customer bereits deutlich schlanker. Wichtig ist, dass beim Refaktorisieren immer kleine Schritte gemacht und mit den vorbereiteten Tests geprüft werden. Natürlich ist das Refaktorisieren dieser Klassen und das Einbauen der Veränderung hier keineswegs schon abgeschlossen. Martin Fowler zeigt in seinem Buch noch weitere Techniken des Refaktorisieren und führt diese und weitere Änderungen zu Ende. 2 Warum Refaktorisieren? Refaktorisierung ist ein Werkzeug, das für verschiedene Zwecke eingesetzt werden kann und hilft, Software im Gri zu behalten. Durch Eliminieren von Redundanz kann die Menge des Codes oft verringert werden. Dies hilft, einen besseren Überlick zu behalten und bei Änderungen diese nur an einer Stelle durchführen zu müssen. Zudem macht eine optimierte Struktur Software leichter verständlich für Programmierer, die Änderungen daran vornehmen müssen und hilft so Zeit zu sparen. Bei refaktorisiertem Code sind die Absichten der einzelnen Methoden besser erkennbar, vor allem wenn darauf geachtet wird, die Konvention eine Methode für eine Aufgabe einzuhalten. Als sinnvoll hat sich auch erwiesen, alles Wichtige direkt im Code zu dokumentieren. So steht das Wissen nicht nur dem aktuellen Programmierer, sondern auch seinen Nachfolgern genau dann zur Verfügung, wenn es nötig ist und hilft beim Verständnis für den Code. Durch die klar erkennbaren Absichten des Codes, die Dokumentation und das bessere Verständnis lassen sich viele Fehler vermeiden, was natürlich auch dazu beiträgt, produktiver zu arbeiten. Dass sich Qualität, Lesbarkeit und Struktur des Codes verbessern, ist oensichtlich. Dass Refaktorisierung auch hilft schneller zu programmieren, ist nicht ganz so schnell erkennbar. Dies ist darauf zurückzuführen, dass Programmmierer sehr viel Zeit darauf verwenden, Fehler in der Software zu nden. Durch das Refaktorisieren in kleinen Schritten und den Tests, die immer wieder angewendet werden, sind Fehler viel schneller zu nden, wodurch sich viel Zeit einsparen lässt. In vielen Firmen ist der Zeitdruck für Neuerungen in der Software hoch, so dass der Refaktorisierung des Codes keine Zeit eingeräumt wird. Zudem ist Refaktorisierung auf den ersten Blick auch mit mehr Aufwand und mehr Kosten

Refactoring 11 verbunden, was für Unternehmen eher dagegen spricht. Trotzdem kommt die Refaktorisierung der bestehenden Software, in welche bereits viel Geld geossen ist, in der Regel billiger, als die ganze Software neu zu programmieren. Zudem ist es für Programmierer, welche später an der Software arbeiten, einfacher, refaktorisierten Code zu verstehen, was für das Unternehmen auch Kosteneinsparungen bedeutet. Bei refaktorisiertem Code ist es oft einfacher, einen Überblick zu gewinnen. Oft werden beim Schreiben der Tests auch Fehler in der bisherigen Software gefunden. Diese können während dem Refaktorisieren behoben werden. 3 Wann wird Refaktorisieren eingesetzt? 3.1 Zeitpunkt des Refaktorisierens Refaktorisieren sollte immer in kleinen Schritten gemacht werden. Es ist besser, nicht Zeit einzuplanen zum Refaktorisieren, sondern Refaktorisieren dann einzusetzen, wenn es hilft etwas anderes einfacher zu machen. In seinem Buch empehlt Fowler die Dreierregel, die dabei helfen kann, den Zeitpunkt zum Refaktorisieren festzulegen. Wenn Sie etwas das erste Mal machen, tun Sie es einfach. Das zweite Mal, wenn Sie etwas ähnliches machen, so scheuen Sie zwar die Wiederholung, aber Sie machen es trotzdem noch einmal. Wenn Sie etwas Ähnliches das dritte Mal tun, refaktorisieren Sie.[1, Seite 46] Häug wird refaktorisiert, wenn Software etwas Neues hinzuzufügt wird. Hier können Anpassungen der Struktur ermöglichen, dass Neues leichter eingefügt werden kann. Das Verbessern der Struktur des Codes dank Refaktorisierung trägt dazu bei, dass Fehler in Programmen gefunden werden können. Code-Reviews in kleinen Teams dienen dazu, Wissen gleichmässig zu verteilen und an jüngere weiterzugeben. Hier können mehrere Personen nützliche Infos beitragen zum Verbessern von Software. Es lohnt sich auch hier, bei Vorschlägen, zu überlegen, ob diese durch Refaktorisieren leicht umzusetzen sind und dies bei guten Vorschlägen direkt zu machen. 3.2 Welcher Code muss Refaktorisiert werden? Duplizierter Code Wenn die gleiche Codestruktur in einem Programm an mehr als einer Stelle vorkommt, sollte immer refaktorisiert und dieser Code vereinigt werden. Kommt der Code in der gleichen Klasse zweimal vor, kann dies sehr einfach durch Methode extrahieren an der einen Stelle gemacht werden. Danach wird die neu erstellte Methode an beiden Orten aufgerufen. Kommt der gleiche Code in verschiedenen Geschwisterklassen oder ähnlicher Code in mehreren Klassen vor, kann auch hier, durch den Einsatz geeigneter Refaktorisierungen, das Programm verbessert werden.

12 Dominique Steiner, Monica De Donato Lange Methode Kurze Methoden machen ein Programm durch bessere Verständlichkeit, gemeinsame Nutzung und Auswahl viel langlebiger. Es ist jedoch sehr wichtig, dass diese kurzen Methoden gute Name haben, so dass der Rumpf nicht gelesen werden muss um zu wissen was gemacht wird. Methoden verkürzen wird durch Methode extrahieren gemacht, hier sollte darauf geachtet werden, dass nicht zu viele Parameter mitgegeben werden. Diese sollten, wenn möglich, durch temporäre Variablen oder direkte Aufrufe ersetzt werden, was mit den entsprechenden Refaktorisierungen einfach umzusetzen ist. Grosse Klasse Grosse Klassen, die zu viel tun, sind meist an zu vielen Instanzvariablen erkennbar. Hier kann oft Klasse extrahieren angewandt werden, um aus Instanzvariablen, die zusammenpassen ein neues Objekt zu erzeugen. In Klassen, die viel Code und sehr lange Methoden enthalten, bendet sich oft auch redundanter Code, der durch kürzere Methoden mehrfach genutzt werden kann. Lange Parameterliste Bei der objektorientierten Programmierung wird nicht mehr alles, was eine Methode benötigt, als Parameter übergeben. Es wird nur soviel übergeben, dass die Methode sich die anderen Informationen selber holen kann. Daher sollen Parameter wenn möglich durch einen direkten Methodenaufruf ersetzt werden, was durch die Refaktorisierung Paramter durch explizite Methode ersetzen erreicht wird. Divergierende Änderungen Divergierende Änderungen lassen sich dadurch erkennen, dass die Struktur der Software es nicht zulässt, Änderungen an einer Stelle einfach vorzunehmen. Dies kann dadurch entstehen, dass einen Klasse häug verändert wird. Müssen bei Änderungen an mehreren Stellen Anpassungen vorgenommen werden, kann es helfen, die Klasse mit Klassse extrahieren zu teilen und zwei Objekte daraus zu machen. 3.3 Schwierigkeiten beim Refaktorisieren Refaktorisieren ist eine sehr gute Methode, doch es gibt Situtationen, bei denen sich Schwierigkeiten ergeben können. Bei Datenbanken bringt die enge Anbindung an Applikationen sowie die Migration von Daten Schwierigkeiten beim Refaktorisieren mit sich. Bei nicht objektorientierten Datenbanken gibt es die Möglichkeit, dies durch eine zusätzliche Softwareschicht zu lösen, da sich die Veränderungen so isolieren lassen. Die Migration von objektorientierte Datenbanken ist mit sehr viel Aufwand verbunden, auch wenn einige Datenbanken automatische Funktionen zur Migration von Objekten anbieten.

Refactoring 13 Das Ändern von öentlichen Schnittstellen (public Methoden) kann Auswirkungen auf nicht vorhersehbare Komponenten haben, da diese oft von anderen Programmen genutzt werden. Viele Refaktorisierungs-Techniken verändern jedoch die Schnittstellen, wie zum Beispiel Methode umbenennen. Daher braucht das Ändern dieser Schnittstellen eine aufwändigen Prozess. Über längere Zeit werden beide Schnittstellen beibehalten, wobei alte als deprecated bezeichnet wird, die nicht mehr verwendet werden soll. Erst nachdem alle Nutzer die Möglichkeit hatten ihre Programme anzupassen, darf die alte Schnittstelle aufgehoben werden. Beim Veröentlichen von Schnittstellen sollte daher gut überlegt werden, welche wirklich geönet werden. Wenn Code sehr schlecht ist und nicht funktioniert, kann nicht refaktorisiert werden. Denn um refaktorisieren zu können, müssen die Tests, welche die Funktionalität prüfen, alle korrekt durchlaufen. In dieser Situation ist es einfacher, den Code neu zu schreiben. Ein weiterer Zeitpunkt, zu dem eine Refaktorisierung des Codes nicht sinnvoll ist, ist kurz vor dem Abgabetermin. Hier kann kein Produktivitätsgewinn mehr herausgeholt werden. 3.4 Refaktorisieren und Design Obwohl sich durch Refaktorisieren das Design verbessern lässt, lohnt es sich, das Design zuerst zu planen und danach zu programmieren. Wenn sich beim Planen verschiedene Möglichkeiten bieten, kann Refaktorisieren mithelfen sich zu entscheiden. Wenn sich nämlich die einfachere Lösung durch Refaktorisieren ohne viel Aufwand in die exiblere, meist aber kompliziertere Lösung umbauen lässt, ist es sinnvoll zu diesem Zeitpunkt die einfache Lösung zu implementieren. Falls es nötig wird, kann diese dann später immer noch zur exibleren Lösung ausgebaut werden. 3.5 Refaktorisieren und Performance Durch das Refaktorisieren wird die Struktur des Programms zwar übersichtlicher, dabei jedoch meist langsamer. Hier wird der Ansatz so gewählt, dass zuerst refaktorisiert wird, danach wird an dem optimierten Code ein Performace Tuning durchgeführt. Die Ausnahme sind Echtzeitsysteme, bei denen für alle Methoden ein Zeitbudget erstellt wird, dass eingehalten werden muss. 4 Refaktorisierungs-Werkzeuge (Tools) 4.1 Allgemein In Entwicklungsumgebungen sind heute Werkzeuge zum Refaktorisieren eingebaut, die viele der erwähnten Techniken unterstützen. Dies erleichtert die Arbeit der Programmierer, da sie nicht mehr auf Hilfsmittel wie Suchen/Ersetzen angewiesen sind, sondern genau auf die anzuwendende Refaktorisierung abgestimmte Methoden nutzen können. Was natürlich hilft Zeit zu sparen, aber auch vermeidet, dass aus Versehen neue Fehler in den Code eingebaut werden.

14 Dominique Steiner, Monica De Donato Table 1. Übersicht über verschieden Werkzeuge zum Refaktorisieren Sprache IDE How To Eclipse integriert in die Grundinstallation von Eclipse Java Refactorit Plattformunabhängiges Plugin für NetBeans, Eclipse und andere IDEs JRefactory Bietet neben Refaktorisierung auch Funktionalitäten für UML Diagramme.NET C#Refactory in Visual Studio integriert, funktioniert ähnlich wie bei Eclipse C/C++ REF++ intergriert in Visual Studio zum Refaktorisieren von C++ Code Visual Basic Refactor for VB Plugin für Visual Studio Delphi ModelMaker In diesem Plugin stehen nur die einfachsten Refaktorisierungen zur Verfügung 4.2 Eclipse Auch Eclipse bietet bereits in der Grundinstallation die gängigen Techniken zum Refaktorisieren an. Diese benden sich im Menu Refactor (Fig. 3 Eclipse- Menu) oder können durch einen rechten Mausklick aufgerufen werden. Das Refaktorisieren von Code mit Eclipse ist sehr einfach, da beim Benutzen die Angaben, die zum Ausführen der jeweiligen Refaktorisierung notwendig sind, in Dialogfenstern (Fig. 4) gemacht werden können. Am Schluss bietet Eclipse eine Vorschau (Fig. 5), in der der aktuelle Code und der zukünftige Code nebeneinander stehen und verglichen werden. 5 Refaktorisieren mit Eclipse Das folgende Beispiel zeigt die Refaktorisierung Methode extrahieren mit Eclipse 3.4. Der Code der verwendet wird, ist aus dem Beispiel im ersten Kapitel. Es geht darum den Switch-Abschnitt, mit Hilfe von Eclipse, automatisiert in eine eigene Methode umzuwandeln. Dazu wird als erstes die Codestelle, die extrahiert werden soll, markiert (Fig. 2). Fig. 2. Markieren der Codestelle

Refactoring 15 Als Nächstes wird im Menu Refactor der Punkt Extract Method... aufgerufen (Fig. 3). Fig. 3. Auswählen des Menupunktes Nun önet sich ein Dialogfenster. Hier wird der Name der neuen Methode angegeben. Zudem besteht die Möglichkeit Parameter, die übergeben werden, zu bearbeiten (Fig. 4). Fig. 4. Dialogfenster zum Eingeben der nötigen Parameter

16 Dominique Steiner, Monica De Donato Bevor die Refaktorisierung vollständig durchgeführt wird, können die Änderungen im nächsten Fenster kontrolliert werden (Fig. 5). Fig. 5. Vorschaufenster zum Kontrollieren der Änderungen Nun ist die Methode extrahiert. Eclipse hat an der markierten Stelle automatisch den Aufruf der neuen Methode eingefügt. Die neue Methode wurde nach den bestehenden Methoden hinzugefügt (Fig. 6). Fig. 6. Änderungen im Code 6 Detaillierte Beschreibung einer Refaktorisierung 6.1 Klasse extrahieren Klassen wachsen mit der Zeit. Es werden laufend mehr Methoden und Daten hinzugefügt, so dass sie gross und schlecht verständlich werden. In diesem Fall

Refactoring 17 sollte eine Klasse zerlegt werden. Dazu werden die Daten und Methoden, die zusammen passen in eine neue Klasse gepackt. Fig. 7. Klasse Person, die in die beiden Klassen Person und TelephoneNumber aufgeteilt wird public class Person { public String getname () { return this. name ; public String gettelephonenumber () { return "(" + this. officeareacode + ") " + this. officenumber ; public String getofficeareacode () { return this. officeareacode ; public void setofficeareacode ( String arg ){ this. officeareacode = arg ; public String getofficenumber () { return this. officenumber ; public void setofficenumber ( String arg ){ this. officenumber = arg ; private String name ; private String officeareacode ; private String officenumber ; In diesem Beispiel ist die Klasse Person gewachsen und enthält Informationen zum Tefonnummer-Verhalten, die in eine eigene Klasse verschoben werden können. Hierfür wird im ersten Schritt die neue Klasse deniert. public class TelephoneNumber { Nun werden die notwendigen Assoziationen gemacht. public class Person {

18 Dominique Steiner, Monica De Donato private TelephoneNumber officetelephone = new TelephoneNumber () ;... Als Nächstes werden mit Feld verschieben die Variablen erstellt. public class Person { public String getname () { return this. name ; public String gettelephonenumber () { return "(" + getofficeareacode () + ") " + this. officenumber ; public String getofficeareacode () { return this. officetelephone. getareacode () ; public void setofficeareacode ( String arg ){ this. officetelephone. setareacode ( arg ); public String getofficenumber () { return this. officenumber ; public void setofficenumber ( String arg ){ this. officenumber = arg ; private TelephoneNumber officetelephone = new TelephoneNumber () ; private String name ; private String officenumber ; Nun werden die Methoden durch Methode verschieben in die neue Klasse verschoben. public class Person { public String getname () { return this. name ; public String gettelephonenumber () { return this. officetelephone. gettelephonenumber () ; public TelephoneNumber getofficetelephone () { return this. officetelephone ; TelephoneNumber officetelephone = new TelephoneNumber () ; private String name ; public class TelephoneNumber { public String gettelephonenumber () {

Refactoring 19 return ("("+ this. areacode +")"); public String getareacode () { return this. areacode ; public void setareacode ( String arg ){ this. areacode = arg ; public String getnumber () { return this. number ; public void setnumber ( String arg ){ this. number = arg ; private String areacode ; private String number ; Zuletzt wird entschieden welche Methoden öentlich gemacht werden. Jetzt haben beide Klassen wieder klar denierte Verantwortlichkeiten die sie wahrnehmen. 7 Unit Tests Geeignete Tests sind etwas vom Wichtigsten beim Refaktorisieren, auch wenn die Refaktorisierungen mit Hilfsmitteln durchgeführt werden. Das Schreiben von Test bringt meist ein erhöhtes Programmiertempo mit sich, da viel weniger Zeit für die Fehlersuche aufgewendet werden muss. Jedes mal beim Verändern des Codes werden die Tests ausgeführt. So sind Fehler viel schneller zu nden, da in den kleinen Schritten nur wenig Code verändert werden muss und dieser dem Programmierer noch präsent ist. Durch das intensive Auseinandersetzen mit dem Code, werden auch Fehler von früher in den Programmen entdeckt und können verbessert werden. Mit dem Schreiben der Tests sollte vor oder mit dem Programm begonnen werden. Geeignet ist, zuerst die Tests zu schreiben und danach die entsprechende Funktionalität zu implementieren. Durch die Konzentration auf die Schnittstelle beim Schreiben der Tests, ist das Implementieren der Funktion meist einfacher, da klar ist, was die Funktion machen muss. Zudem ist eindeutig, dass die Funktion fertig implementiert ist, sobald der Test funktioniert. Um jedoch das Refaktorisieren sinnvoll mit Tests unterstützen zu können, müssen diese so angelegt werden, dass sie vollständig automatisiert sind, dass heisst jeder Test muss sich selbst überprüfen und zurückgeben, ob er korrekt durchgelaufen ist oder nicht. Üblich ist, dass pro Klasse eine Testklasse geschrieben wird, welche die Methoden überprüft. Gut geeignet dazu ist das JUnit Framework. Nach dem Durchführen der Test werden die Resultate in der Konsole oder im GUI ausgegeben. Das GUI stellt die Resultate mit roten oder grünen Balken graphisch

20 Dominique Steiner, Monica De Donato dar, so ist auf einen Blick sichtbar, ob die Tests erfolgreich durchgelaufen sind. Während dem Refaktorisieren werden nur die Test, zu der veränderten Programmstelle ausgeführt. Wenn immer alle Tests durchgeführt würden, wäre dies ein zu grosser Zeitverlust. Die gesamten Test werden meist über Nacht durchgeführt, wenn niemand dadurch bei der Arbeit gestört wird. 8 Fazit Refaktorisierung ist eine hervorragende Technik, die heute noch viel zu wenig eingesetzt wird. Es wäre wünschenswert, dass sie sich weiter verbreiten und vermehrt zum Einsatz kommen würde. Dazu müssten sicher bei vielen Leuten erst Vorurteile über Aufwand und Kosten abgebaut und der Gewinn, der mit Refaktorisieren gemacht werden kann, aufgezeigt werden. Oft müsste dazu wohl erst eine solide Testumgebung aufgebaut werden, was für den Anfang einen Mehraufwand bedeutet, der sich jedoch, nach der Umstellungsphase durch produktiveres Arbeiten auszahlen wird. Als Programmierer ist es sinnvoll, sich die Technik des Refaktorisierens anzueignen, um sie zum produktivieren Arbeiten nutzen zu können. Mit den Tools, die heute zum Refaktorisieren zur Verfügung stehen, können die verschiedenen Refaktorisierungen sehr einfach und schnell angewendet werden. Dadurch wird der eigene Code übersichtlicher und besser verständlich für sich selber und andere Programmierer. References 1. Fowler, M.: Refactoring. Wie Sie das Design vorhandener Software verbessern. Addison-Wesley Verlag (Deutschland) GmbH, Bonn (2005) 2. Fowler, M.: Refactoring. Improving The Design of Existing Code. Addison-Wesley Publishing Co., Inc., Reading, MA 01867 (1999) 3. Fowler, M.,: Refactoring. Online, http://www.refactoring.com