Skip to content

Latest commit

 

History

History
477 lines (335 loc) · 12.5 KB

refactoring.md

File metadata and controls

477 lines (335 loc) · 12.5 KB
archetype title linkTitle author readings tldr outcomes quizzes youtube fhmedia challenges
lecture-cg
Refactoring
Refactoring
Carsten Gips (HSBI)
key
Fowler2011
key comment
Inden2013
Kapitel 11: Refactorings
Refactoring bedeutet Änderung der inneren Struktur des Codes ohne Beeinflussung äußeren Verhaltens. Mit Hilfe von Refactoring kann man Code Smells beheben, und Lesbarkeit, Verständlichkeit und Wartbarkeit von Software verbessern. Es ist wichtig, immer nur einzelne Schritte zu machen und anschließend die Testsuite laufen zu lassen, damit nicht versehentlich Fehler oder Verhaltensänderungen beim Refactoring eingebaut werden. Prinzipiell kann man Refactoring manuell mit Search&Replace durchführen, aber es bietet sich an, hier die IDE-Unterstützung zu nutzen. Es stehen verschiedene Methoden zur Verfügung, die nicht unbedingt einheitlich benannt sein müssen oder in jeder IDE vorkommen. Zu den häufig genutzten Methoden zählen _Rename_, _Extract_, _Move_ und _Push Up/Pull Down_.
k2
Begriff, Notwendigkeit und Vorgehen des/beim Refactoring
k2
Bedeutung kleiner Schritte beim Refactoring
k2
Bedeutung einer sinnvollen Testsuite beim Refactoring
k2
Refactoring: Nur innere Struktur ändern, nicht äußeres Verhalten!
k3
Anwendung der wichtigsten Refactoring-Methoden: _Rename_, _Extract_, _Move_, _Push Up/Pull Down_
link name
VL Refactoring
link name
Demo Refactoring: Rename
link name
Demo Refactoring: Encapsulate
link name
Demo Refactoring: Extract Method
link name
Demo Refactoring: Move Method
link name
Demo Refactoring: Pull up
Betrachten Sie das [Theatrical Players Refactoring Kata](https://github.com/emilybache/Theatrical-Players-Refactoring-Kata). Dort finden Sie im Unterordner [java/](https://github.com/emilybache/Theatrical-Players-Refactoring-Kata/tree/main/java) einige Klassen mit unübersichtlichem und schlecht strukturierten Code. Welche _Bad Smells_ können Sie hier identifizieren? Beheben Sie die Smells durch die _schrittweise Anwendung_ von den aus der Vorlesung bekannten Refactoring-Methoden. Denken Sie auch daran, dass Refactoring immer durch eine entsprechende Testsuite abgesichert sein muss - ergänzen Sie ggf. die Testfälle.

Was ist Refactoring?

Refactoring ist, wenn einem auffällt, daß der Funktionsname foobar ziemlich bescheuert ist, und man die Funktion in sinus umbenennt.

\hfill\ [Quelle: "356: Refactoring" by Andreas Bogk on Lutz Donnerhacke: "Fachbegriffe der Informatik"]{.origin}

\pause \bigskip \vfill

Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour.

\hfill\ [Quelle: [@Fowler2011, p. 53]]{.origin}

::: notes Refactoring: Änderungen an der [inneren Struktur]{.alert} einer Software

  • Beobachtbares (äußeres) Verhalten ändert sich dabei nicht
    • Keine neuen Features einführen
    • Keine Bugs fixen
    • Keine öffentliche Schnittstelle ändern (Anmerkung: Bis auf Umbenennungen oder Verschiebungen von Elementen innerhalb der Software)
  • Ziel: Verbesserung von Verständlichkeit und Änderbarkeit :::

Anzeichen, dass Refactoring jetzt eine gute Idee wäre

  • Code "stinkt" (zeigt/enthält Code Smells)

    ::: notes Code Smells sind strukturelle Probleme, die im Laufe der Zeit zu Problemen führen können. Refactoring ändert die innere Struktur des Codes und kann entsprechend genutzt werden, um die Smells zu beheben. :::

\bigskip

  • Schwer erklärbarer Code

    ::: notes Könnten Sie Ihren Code ohne Vorbereitung in der Abgabe erklären? In einer Minute? In fünf Minuten? In zehn? Gar nicht?

    In den letzten beiden Fällen sollten Sie definitiv über eine Vereinfachung der Strukturen nachdenken. :::

  • Verständnisprobleme, Erweiterungen

    ::: notes Sie grübeln in der Abgabe, was Ihr Code machen sollte?

    Sie überlegen, was Ihr Code bedeutet, um herauszufinden, wo Sie die neue Funktionalität anbauen können?

    Sie suchen nach Codeteilen, finden diese aber nicht, da die sich in anderen (falschen?) Stellen/Klassen befinden?

    Nutzen Sie die (neuen) Erkenntnisse, um den Code leichter verständlich zu gestalten. :::

\bigskip \vfill

::: center

"Three strikes and you refactor."

\hfill\ [Quelle: [@Fowler2011, p. 58]: "The Rule of Three"]{.origin} :::

::: notes Wenn Sie sich zum dritten Mal über eine suboptimale Lösung ärgern, dann werden Sie sich vermutlich noch öfter darüber ärgern. Jetzt ist der Zeitpunkt für eine Verbesserung.

Schauen Sie sich die entsprechenden Kapitel in [@Passig2013] und [@Fowler2011] an, dort finden Sie noch viele weitere Anhaltspunkte, ob und wann Refactoring sinnvoll ist. :::

Bevor Sie loslegen ...

  1. Unit Tests schreiben
    • Normale und ungültige Eingaben
    • Rand- und Spezialfälle

\smallskip

  1. Coding Conventions einhalten
    • Sourcecode formatieren (lassen)

\bigskip \smallskip

  1. Haben Sie die [fragliche Codestelle auch wirklich verstanden]{.alert}?!

Vorgehen beim Refactoring

::: notes

Überblick über die Methoden des Refactorings

Die Refactoring-Methoden sind nicht einheitlich definiert, es existiert ein großer und uneinheitlicher "Katalog" an möglichen Schritten. Teilweise benennt jede IDE die Schritte etwas anders, teilweise werden unterschiedliche Möglichkeiten angeboten.

Zu den am häufigsten genutzten Methoden zählen

  • Rename Method/Class/Field
  • Encapsulate Field
  • Extract Method/Class
  • Move Method
  • Pull Up, Push Down (Field, Method)

Best Practice

Eine Best Practice (oder nennen Sie es einfach eine wichtige Erfahrung) ist, beim Refactoring langsam und gründlich vorzugehen. Sie ändern die Struktur der Software und können dabei leicht Fehler oder echte Probleme einbauen. Gehen Sie also langsam und sorgsam vor, machen Sie einen Schritt nach dem anderen und sichern Sie sich durch eine gute Testsuite ab, die Sie nach jedem Schritt erneut ausführen: Das Verhalten der Software soll sich ja nicht ändern, d.h. die Tests müssen nach jedem einzelnen Refactoring-Schritt immer grün sein (oder Sie haben einen Fehler gemacht). :::

  • Kleine Schritte: immer nur eine Änderung zu einer Zeit

  • Nach jedem Refactoring-Schritt Testsuite laufen lassen

    => Nächster Refactoring-Schritt erst, wenn alle Tests wieder "grün"

  • Versionskontrolle nutzen: Jeden Schritt einzeln committen

Refactoring-Methode: Rename Method/Class/Field

::: notes

Motivation

Name einer Methode/Klasse/Attributs erklärt nicht ihren Zweck.

Durchführung

Name selektieren, "Refactor > Rename"

Anschließend ggf. prüfen

Aufrufer? Superklassen?

Beispiel

:::

Vorher

public String getTeN() {}

Nachher

public String getTelefonNummer() {}

Refactoring-Methode: Encapsulate Field

::: notes

Motivation

Sichtbarkeit von Attributen reduzieren.

Durchführung

Attribut selektieren, "Refactor > Encapsulate Field"

Anschließend ggf. prüfen

Superklassen? Referenzen? (Neue) JUnit-Tests?

Beispiel

:::

Vorher

int cps;

public void printDetails() {
    System.out.println("Credits: " + cps);
}

Nachher

private int cps;

int getCps() { return cps; }
void setCps(int cps) {  this.cps = cps;  }

public void printDetails() {
    System.out.println("credits: " + getCps());
}

Refactoring-Methode: Extract Method/Class

::: notes

Motivation

  • Codefragment stellt eigenständige Methode dar
  • "Überschriften-Code"
  • Code-Duplizierung
  • Code ist zu "groß"
  • Klasse oder Methode erfüllt unterschiedliche Aufgaben

Durchführung

Codefragment selektieren, "Refactor > Extract Method" bzw. "Refactor > Extract Class"

Anschließend ggf. prüfen

  • Aufruf der neuen Methode? Nutzung der neuen Klasse?
  • Neue JUnit-Tests nötig? Veränderung bestehender Tests nötig?
  • Speziell bei Methoden:
    • Nutzung lokaler Variablen: Übergabe als Parameter!
    • Veränderung lokaler Variablen: Rückgabewert in neuer Methode und Zuweisung bei Aufruf; evtl. neue Typen nötig!

Beispiel

:::

Vorher

public void printInfos() {
    printHeader();
    // Details ausgeben
    System.out.println("name:    " + name);
    System.out.println("credits: " + cps);
}

Nachher

public void printInfos() {
    printHeader();
    printDetails();
}
private void printDetails() {
    System.out.println("name:    " + name);
    System.out.println("credits: " + cps);
}

Refactoring-Methode: Move Method

::: notes

Motivation

Methode nutzt (oder wird genutzt von) mehr Eigenschaften einer fremden Klasse als der eigenen Klasse.

Durchführung

Methode selektieren, "Refactor > Move" (ggf. "Keep original method as delegate to moved method" aktivieren)

Anschließend ggf. prüfen

  • Aufruf der neuen Methode (Delegation)?
  • Neue JUnit-Tests nötig? Veränderung bestehender Tests nötig?
  • Nutzung lokaler Variablen: Übergabe als Parameter!
  • Veränderung lokaler Variablen: Rückgabewert in neuer Methode und Zuweisung bei Aufruf; evtl. neue Typen nötig!

Beispiel

:::

Vorher

public class Kurs {
    int cps;
    String descr;
}

public class Studi extends Person {
    String name;
    int cps;
    Kurs kurs;

    public void printKursInfos() {
        System.out.println("Kurs:    " + kurs.descr);
        System.out.println("Credits: " + kurs.cps);
    }
}

::: slides

Refactoring-Methode: Move Method (cnt.)

:::

Nachher

public class Kurs {
    int cps;
    String descr;

    public void printKursInfos() {
        System.out.println("Kurs:    " + descr);
        System.out.println("Credits: " + cps);
    }
}

public class Studi extends Person {
    String name;
    int cps;
    Kurs kurs;

    public void printKursInfos() { kurs.printKursInfos(); }
}

Refactoring-Methode: Pull Up, Push Down (Field, Method)

::: notes

Motivation

  • Attribut/Methode nur für die Oberklasse relevant: Pull Up
  • Subklassen haben identische Attribute/Methoden: Pull Up
  • Attribut/Methode nur für eine Subklasse relevant: Push Down

Durchführung

Name selektieren, "Refactor > Pull Up" oder "Refactor > Push Down"

Anschließend ggf. prüfen

Referenzen/Aufrufer? JUnit-Tests?

Beispiel

:::

Vorher

public class Person { }

public class Studi extends Person {
    String name;
    public void printDetails() { System.out.println("name:    " + name); }
}

Nachher

public class Person { protected String name; }

public class Studi extends Person {
    public void printDetails() { System.out.println("name:    " + name); }
}

Wrap-Up

Behebung von Bad Smells durch Refactoring

\smallskip

=> Änderung der inneren Struktur ohne Beeinflussung des äußeren Verhaltens

\bigskip

  • Verbessert Lesbarkeit, Verständlichkeit, Wartbarkeit
  • Immer nur kleine Schritte machen
  • Nach jedem Schritt Testsuite laufen lassen
  • Katalog von Maßnahmen, beispielsweise Rename, Extract, Move, Push Up/Pull Down, ...
  • Unterstützung durch IDEs wie Eclipse, Idea, ...

::: slides

LICENSE

Unless otherwise noted, this work is licensed under CC BY-SA 4.0.

\bigskip

Exceptions

  • Citation "Refactoring ist, wenn ...": "356: Refactoring" by Andreas Bogk on Lutz Donnerhacke: "Fachbegriffe der Informatik"
  • Citation "Refactoring (noun): a change ...": [@Fowler2011, p. 53]
  • Citation "The Rule of Three": [@Fowler2011, p. 58] :::