Programmierpraktikum SoSe 2024, Bachelor Informatik, FU Berlin
ProPra2024 > Bestandscode > Refactoring-Grundlagen > extraction_of_code

Extrahieren von Code

Idea

Ziel

  • Ich verstehe, wie ich Variablen, Methoden und Klassen extrahieren kann.
  • Ich verstehe, wie ich meinen Code verändern muss, damit diese Änderung funktioniert.
  • Ich verstehe, wann es nützlich ist, diese Refaktorierungen durchzuführen.

Hintergrund

Code schreiben kann am Anfang sehr einfach sein. Etwas Datenstruktur hier, etwas Funktionalität da… und irgendwann hat man ein Produkt, das funktioniert. Die innere Struktur ist zu diesem Zeitpunkt aber oft nicht gut: Stellenweise zu wenig Abstraktion und Modularität, stellenweise gibt es Redundanz oder der Code ist einfach verworren.

Wenn man das nicht aufräumt, wird die künftige Fortentwicklung eines Programms sehr schnell sehr schwierig. Aber was heißt "Aufräumen"? Und wie geht das? Ein wichtiger Ansatz dafür ist die Bildung von Abstraktionen durch Extraktion von Teilen.

Das ist unser Thema hier.

Detailed

Arbeitsschritte

Diese Aufgabe teilt sich in einen Theorieteil und einen Praxisteil. Erstellen Sie zunächst ein Markdown-Dokument Extraction-Of-Code.md und erstellen Sie darin zwei Überschriften "Theorie" und "Praxis".

Teil 1: Die Theorie

  • Erstellen Sie im Abschnitt "Theorie" die drei Überschriften "Extraktion von Variablen", "Extraktion von Methoden" und "Extraktion von Klassen".
  • Lesen Sie anschließend die drei Artikel Extract Variable, Extract Method und Extract Class, und beantworten Sie anschließend zu jedem Artikel jeweils die folgenden Fragen:
    • 1 Welches Problem löst diese Refaktorierung?
    • 2 Wie wird diese Refaktorierung per Hand durchgeführt?
    • 3 Auf welche Fallstricke soll man bei dieser Refaktorierung achten?
    • 4 Recherchieren und beschreiben Sie, wie Ihre IDE Ihnen hilft diese Refaktorierung durchzuführen.
      Bietet Ihre IDE Ihnen hierfür eine Funktion an?
      Wie löst man diese Funktion aus (per Menü-Klick, per Tastatur)?

Teil 2: Die Praxis

Folgend werden wir einen einfachen Python-Code nehmen und die drei Extraktionen nacheinander darauf anwenden. Es ist sinnvoll die beschriebenen Extraktionen von Hand durchzuführen.

  • Erstellen Sie eine Datei Extraction-Of-Code.py und kopieren Sie den folgenden Python-Code hinein:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import random

MONTHS = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')
month = random.choice(MONTHS)


def what_to_eat(month):
    if month.lower().endswith("r") or month.lower().endswith("ary"):
        print(f"{month}: oysters")
    elif 8 > MONTHS.index(month) > 4:
        print(f"{month}: tomatoes")
    else:
        print(f"{month}: asparagus")
  • Machen Sie einen Commit mit der Datei Extraction-Of-Code.py.
  • 1 git -P show HEAD
  • Der Code nimmt einen Monat als Eingabe und gibt zurück, welches Essen in diesem Monat Saison hat. Zwar ist der Code einfach gehalten, allerdings ist er schlecht testbar oder erweiterbar.
  • 1 Extrahieren Sie die Ausdrücke month.lower().endswith("r"), month.lower().endswith("ary") und 8 > MONTHS.index(month) > 4 in eigene Variablen. Achten Sie darauf den Variablen ausdrucksstarke Namen zu geben!
  • Machen Sie einen Commit mit der Datei Extraction-Of-Code.py.
  • 2 git -P show HEAD
  • Die Logik des Programms ist jetzt schon deutlich einfacher zu verstehen. Allerdings sind Erweiterung und Testbarkeit immer noch eher schwierig.
  • 2 Extrahieren Sie die Boolschen Ausdrücke der if-Ausdrücke in eigene Funktionen. Benutzen Sie hierfür die in 1 eingeführten Variablen. Achten Sie darauf ausdrucksstarke Funktionsnamen zu vergeben!
  • Machen Sie einen Commit mit der Datei Extraction-Of-Code.py.
  • 3 git -P show HEAD
  • Aktuell wird beim Durchlauf des if-elif-else-Ausdrucks in jedem Schritt eine Funktion aufgerufen. Aus verschiedensten Gründen kann es aber nützlich sein diese Aufrufe schon vor Beginn des Ausdrucks erledigt zu haben, z. B. weil man das Ergebnis cachen möchte oder sich sehr viel Logik parallelisieren lässt. Es kann nützlich sein an dieser Stelle die Extraktionen von Variablen und Funktionen zu verbinden.
  • 3 Extrahieren Sie die Funktionsaufrufe im if- bzw. elif-Ausdruck in eigene Variablen. Achten Sie darauf ausdrucksstarke Variablennamen zu vergeben!
  • Machen Sie einen Commit mit der Datei Extraction-Of-Code.py.
  • 4 git -P show HEAD
  • Das Programm sieht jetzt aufgeräumt auf. Es existieren Funktionen, die die beiden Boolschen Werte für die Austern- und Tomatensaison berechnen und diese Ergebnisse sind mittels einer Variable erreichbar. Um eine gute Testbarkeit zu erreichen, wären aber Klassen sehr angenehm. (Für unser Miniprogramm ist der nächste Schritt übertrieben, aber für ein größeres Programm wäre er unter Umständen sinnvoll. Wir machen ihn zu Übungszwecken.)
  • 4 Erstellen Sie die zwei Klassen OystersGood und TomatoesGood. Bilden Sie die Funktionen aus 2 in diesen Klassen ab.
    Implementieren Sie hierfür die Methode __init__(self, month), um die Logik der Funktionen abzubilden und __bool__(self, month), um den Rückgabewert der Funktionen abzubilden. Ersetzen Sie danach die Funktionsaufrufe aus 3 durch OystersGood(month) bzw. TomatoesGood(month).
  • Machen Sie einen Commit mit der Datei Extraction-Of-Code.py.
  • 5 git -P show HEAD
  • Das Programm hat nun einen ziemlichen Wandel hingelegt. Mithilfe dieser Klassen ist optimale Testbarkeit gegeben, mehr dazu in Testen.
  • 5 Welche der Varianten des Programms gefällt Ihnen am besten? Begründen Sie kurz.
Information

Abgabe

Geben Sie ein Markdown-Dokument ab mit knappen Antworten zu den oben gestellten Fragen 1, 2, … Geben Sie diese Marker mit an.
Geben Sie ggf. Beispiele oder benutzte Quellen an.

Geben Sie ein Kommandoprotokoll ab, das genau nur die Eingaben und Ausgaben der obigen Kommandos 1, 2, … enthält. Entfernen Sie vor Abgabe eventuelle Fehlversuche und sonstige zusätzliche Kommandos aus dem Protokoll.

Geben Sie den Quellcode ab, wie er am Ende der Aufgabe vorliegt.