Eine Klasse, die rechnet, formatiert und speichert
Ein Entwurfsprinzip ist eine Faustregel, die Klassen leichter änderbar macht. Zwei davon gehören zum Werkzeug jeder Entwicklerin und jedes Entwicklers: eine Verantwortung je Klasse und offen für Erweiterung.
Ein Notenbericht rechnet einen Durchschnitt, macht daraus Text und legt ihn ab. Wer nur den Text ändern will, muss eine Klasse öffnen, die auch rechnet und speichert. Dabei geht schnell etwas kaputt.
Am Ende dieser Lektion
Notiz
Ich kann Code erkennen, der einer Klasse zwei Aufgaben gibt oder sich schwer erweitern lässt.
Schlüsselwörter
| Begriff | Was er bedeutet |
|---|---|
| Verantwortung | eine Aufgabe, für die eine Klasse allein zuständig ist |
| Änderungsgrund | ein Wunsch, der eine Klasse ändern muss |
| Kohäsion | wie eng die Teile einer Klasse zusammengehören |
| Erweiterung | neues Verhalten, das neuen Code statt Änderungen braucht |
| Schnittstelle | die Methoden, die andere aufrufen dürfen |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Eine Klasse hat nur einen Änderungsgrund
Das Prinzip der einzigen Verantwortung lautet: Eine Klasse hat genau eine Aufgabe und damit genau einen Änderungsgrund. Der Notenbericht hat drei: die Rechnung, den Text und die Ablage.
Jede Änderung an einer Aufgabe kann die beiden anderen stören, weil sie im selben Code stehen.
Ein Bericht mit drei Aufgaben
class Notenbericht:
def __init__(self, noten):
self.noten = noten
def durchschnitt(self):
summe = 0
for note in self.noten:
summe = summe + note
return summe // len(self.noten)
def als_text(self):
return "Schnitt: " + str(self.durchschnitt())
def speichern(self, ablage):
ablage.append(self.als_text())
ablage = []
bericht = Notenbericht([12, 8, 10])
bericht.speichern(ablage)
print(ablage)
# Diese Klasse hat drei Gründe, sich zu ändern. Welche?
Welche Klasse sich bei welchem Wunsch ändert
| Wunsch | Vorher | Nach dem Aufteilen |
|---|---|---|
| Der Schnitt soll anders gerechnet werden | Notenbericht | Rechner |
| Der Text soll kürzer sein | Notenbericht | Textform |
| Die Ablage soll in eine Datei schreiben | Notenbericht | Ablage |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein häufiger Fehler
Warnung
Viele glauben, eine Verantwortung sei eine einzelne Methode, und zerlegen alles in Klassen mit je einer Zeile. Eine Verantwortung ist ein Änderungsgrund, kein Methodenzähler. Eine Klasse darf viele Methoden haben, solange alle derselben Aufgabe dienen.
Neues Verhalten soll neuen Code brauchen, keine Änderung
Das zweite Prinzip heißt offen für Erweiterung, geschlossen für Änderung. Neues Verhalten kommt durch eine neue Klasse hinzu. Die alte Klasse bleibt unberührt.
Der aufgeteilte Bericht ruft nur als_text auf. Eine neue Textform mit derselben Methode passt ohne Eingriff in Bericht.
Aufgeteilt: eine neue Textform ohne Eingriff
class Rechner:
def durchschnitt(self, noten):
return sum(noten) // len(noten)
class Langtext:
def als_text(self, wert):
return "Schnitt: " + str(wert)
class Kurztext:
def als_text(self, wert):
return str(wert)
class Bericht:
def __init__(self, rechner, textform):
self.rechner = rechner
self.textform = textform
def erstellen(self, noten):
wert = self.rechner.durchschnitt(noten)
return self.textform.als_text(wert)
print(Bericht(Rechner(), Langtext()).erstellen([12, 8, 10]))
print(Bericht(Rechner(), Kurztext()).erstellen([12, 8, 10]))
Die Aufgaben einer Klasse im Lab zählen
Im Lab schreiben Sie die Klasse Notenbericht in die Datei bericht.py. Danach fragen Sie mit grep, welche Methoden sie hat.
Der Befehl grep -o zeigt nur den Teil der Zeile, der zum Muster passt. So erscheint pro Methode eine kurze Zeile: erst def __init__, dann die drei Aufgaben.
Den Ordner anlegen
mkdir -p ~/oop-laborHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Hineingehen
cd ~/oop-laborHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Klasse mit drei Aufgaben schreiben
printf 'class Notenbericht:\n def __init__(self, noten):\n self.noten = noten\n\n def durchschnitt(self):\n return sum(self.noten) // len(self.noten)\n\n def als_text(self):\n return "Schnitt: " + str(self.durchschnitt())\n\n def speichern(self, ablage):\n ablage.append(self.als_text())\n' > bericht.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Methoden der Klasse auflisten
grep -o 'def [a-z_]*' bericht.pyHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Drei Aufgaben stehen in einer Klasse
Die Ausgabe nennt vier Zeilen: def __init__, def durchschnitt, def als_text und def speichern. Die erste richtet das Objekt ein, die anderen drei sind die Aufgaben.
Rechnen, Formatieren und Speichern haben nichts miteinander zu tun. Das ist der Hinweis, dass die Klasse in drei zerfallen sollte.