LU07b - Code Smells erkennen

Ein Code Smell ist kein Fehler. Das Programm läuft, die Tests sind grün - und trotzdem stimmt etwas nicht: Der Code ist schwer zu lesen, schwer zu ändern oder schwer zu testen. Der Smell ist der Hinweis, an welcher Stelle sich ein Refactoring lohnt.

Warum «Geruch»? Weil er ein Verdacht ist, kein Beweis. Eine Funktion mit 40 Zeilen kann völlig in Ordnung sein. Ein Smell sagt nur: Schauen Sie hier genauer hin.

Die Funktion tut mehr als eine Sache. Erkennbar an Kommentaren, die Abschnitte markieren («# jetzt die Summe», «# jetzt die Ausgabe») - das sind die Schnittlinien.

def auswertung(datei):
    # einlesen
    ...
    # filtern
    ...
    # rechnen
    ...
    # formatieren und ausgeben
    ...

Technik: Extract Function

Dieselbe Logik steht an mehreren Stellen. Beim nächsten Fix wird eine davon vergessen.

netto_a = round(preis_a * 1.081, 2)
netto_b = round(preis_b * 1.081, 2)

Technik: Extract Function, bei Varianten Parameterize Function

Eine Zahl oder ein String steht mitten im Code, und niemand weiss, was sie bedeutet.

if kunde["punkte"] > 500:
    rabatt = betrag * 0.15

Technik: Replace Magic Number with Named Constant

Fünf oder mehr Parameter, die inhaltlich zusammengehören. Beim Aufruf vertauscht man irgendwann zwei davon - und Python merkt es nicht.

def zeilenpreis(artikel, menge, einzelpreis, rabatt, waehrung, mwst):
    ...

Technik: Introduce Parameter Object (frozen Dataclass, siehe LU02e)

Der eigentliche Fall steht ganz innen, eingerückt hinter drei if. Die Sonderfälle verdecken die Regel.

if kunde is not None:
    if kunde["aktiv"]:
        if betrag >= 100:
            return betrag * 0.1

Technik: Guard Clauses (Replace Nested Conditional with Guard Clauses)

Der Kommentar erklärt, was eine Zeile tut, weil die Zeile es selbst nicht sagt.

# prüft ob der Kunde volljährig und aktiv ist
if k["a"] >= 18 and k["s"] == 1:
    ...

Technik: Extract Variable oder Extract Function mit sprechendem Namen. Ein guter Kommentar erklärt das Warum, nicht das Was.

Die Funktion liest oder schreibt eine Variable ausserhalb ihrer selbst. Damit hängt ihr Ergebnis von der Programmgeschichte ab - genau das, was LU02b als unpure beschreibt.

total = 0
 
def add_to_total(x):
    global total
    total += x

Technik: Zustand als Parameter hereingeben und als Rückgabewert herausgeben, alternativ ein Closure (LU05c)

Eine Funktion liefert einen Wert und verändert nebenbei etwas. Man kann sie nicht aufrufen, nur um nachzuschauen.

def entnehmen(bestand, artikel):
    bestand[artikel] -= 1      # verändert
    return bestand[artikel]    # und liefert

Technik: Separate Query from Modifier

Am Aufruf ist nicht erkennbar, was passiert: exportieren(daten, True) - was ist True?

Technik: Zwei Funktionen mit sprechenden Namen, oder Keyword-Argument erzwingen (exportieren(daten, mit_header=True))

Eine Schleife, die nur filtert, umformt oder aufsummiert - das ist der Smell mit dem stärksten Bezug zu diesem Modul.

resultat = []
for e in daten:
    if e["aktiv"]:
        resultat.append(e["name"].upper())

Technik: Replace Loop with Pipeline (LU07d)

Smell Woran erkennbar Technik
Lange Funktion Abschnittskommentare, mehr als ein Zweck Extract Function
Duplizierter Code dieselbe Zeile mehrfach Extract / Parameterize Function
Magic Number nackte Zahl oder String im Code Named Constant
Lange Parameterliste 5+ Parameter, die zusammengehören Introduce Parameter Object
Verschachtelte Bedingungen drei Einrückungsebenen und mehr Guard Clauses
Kommentar als Krücke Kommentar erklärt das Was Extract Variable / Function
Globaler Zustand global, Modulvariable wird geschrieben Parameter und Rückgabewert, Closure
Query und Modifier vermischt Funktion liefert und verändert Separate Query from Modifier
Boolean-Flag True / False als Argument zwei Funktionen oder Keyword-Argument
Schleife mit Akkumulator resultat = [] plus append Replace Loop with Pipeline

Smell ist nicht gleich Schuld. Ein Smell rechtfertigt einen Blick, nicht automatisch eine Änderung. Wer jede Dreizeiler-Schleife in eine verschachtelte Comprehension presst, hat den Code nicht besser gemacht, sondern nur anders unleserlich. Die Frage lautet immer: Ist die Version danach für den nächsten Leser einfacher?


© Kevin Maurizi

  • modul/m323/learningunits/lu07/smells.txt
  • Zuletzt geändert: 2026/09/09 11:06
  • von admin