Kapitel 5 · Übung
Gültigen Objektzustand schützen
Den gültigen Zustand eines Objekts festlegen
Die Klasse Question aus Kapitel 4 erfüllt ihre Aufgabe. Sie hält einen Fragetext und die zugehörige Antwort zusammen und entscheidet selbst, ob eine eingegebene Antwort richtig ist:
Sieh dir nun an, was diese Klasse außerdem akzeptiert:
Eine Frage ohne Fragetext und ohne Antwort. Python erzeugt sie ohne Einwand, und is_correct("") meldet bereitwillig True. Alle Lernenden, die eine leere Antwort abgeben, liegen damit richtig.
Die Klasse hat die Daten zusammengefasst. Dass diese Daten sinnvoll sind, hat sie nicht zugesichert.
Regeln, die immer gelten sollen
Überlege, was gelten muss, damit ein Question-Objekt überhaupt nützlich ist:
Der Fragetext ist nicht leer, denn die lernende Person muss etwas lesen können.
Die erwartete Antwort ist nicht leer, denn sonst zählt eine leere Eingabe als richtig.
Die vergebenen Punkte sind positiv, denn dieses Quiz vergibt für jede richtige Antwort Punkte. Andere Quizkonzepte könnten bewusst Fragen ohne Wertung vorsehen.
Solche Regeln haben einen Namen. Eine Regel, die für ein Objekt von seiner Erzeugung bis zu seiner Entfernung gelten soll, heißt Invariante: das, was sich nicht ändert.
Wichtiger als das Wort ist die Gewohnheit dahinter. Frage beim Entwerfen einer Klasse, was für sie immer gelten muss. Kapitel 4 fragte, was ein Objekt enthalten und was es tun soll. Dieses Kapitel ergänzt eine dritte Frage: In welchen Zustand darf es niemals geraten?
Welche Aussage beschreibt eine Invariante eines Question-Objekts?
Niemand passt auf
Hier kommt der unangenehme Teil. Selbst ein korrekt erzeugtes Question-Objekt kann danach beschädigt werden:
Diese Version von Question prüft spätere Attributzuweisungen nicht. Eben war das Objekt noch gültig, jetzt ist es kaputt, und nirgends wurde ein Problem gemeldet.
Das ist kein Fehler in Python. Gewöhnliche Python-Attribute nehmen einen neuen Wert an, wenn Code ihn zuweist. Was wir gleich ergänzen, ist eine bewusste Einschränkung durch die Person, die die Klasse schreibt, und zwar dort, wo ein falscher Wert später ernsthafte Probleme verursachen würde.
Wo das Problem tatsächlich auftritt
Die Beschädigung oben geschieht unbemerkt. Genau das ist das Problem. Ein beschädigtes Question-Objekt scheitert nicht dort, wo es beschädigt wurde. Es scheitert später an anderer Stelle, in Code, der ihm mit gutem Grund vertraut:
for question in question_list:
print(question.prompt.upper())
Ist ein Fragetext None, löst diese Schleife AttributeError aus, und der Traceback zeigt hierher. Die Schleife trifft aber keine Schuld. Der Fehler entstand dort, wo dieses None zugewiesen wurde, möglicherweise in einer anderen Funktion, die eine andere Person an einem anderen Tag geschrieben hat.
Der Rest dieses Kapitels schließt diese Lücke:
Lektion 2 verhindert, dass ein ungültiges Objekt überhaupt erzeugt wird.
Lektion 3 ersetzt lose Attributzuweisungen durch Methoden mit einer klaren Bedeutung.
Lektionen 4 und 5 trennen die Zusagen einer Klasse von ihrer aktuellen Datenspeicherung.
Lektion 6 erlaubt eine einzelne, unabhängig gültige Änderung über einen prüfenden Setter.
Lektion 7 stellt sicher, dass eine Änderung zusammengehöriger Attribute entweder vollständig geschieht oder gar nicht.
Regeln benennen, bevor du sie durchsetzt
Bevor du neue Syntax schreibst, hilft es, die Regeln klar auszusprechen. Im Übungseditor stehen eine Question-Klasse mit einem zusätzlichen Attribut points und eine Funktion problems(question), die eine Liste mit Beschreibungen aller Probleme einer Frage zurückgeben soll.
Diese Funktion verwendet nichts Neues. Sie besteht aus gewöhnlichen if-Anweisungen über Attribute, die du bereits lesen kannst. Beim Schreiben merkst du, ob dir die Regeln selbst klar sind. Genau diese Vorbereitung brauchst du, bevor du die Klasse sie durchsetzen lässt.
Aufgabe
Vervollständige problems, sodass die Funktion eine Liste kurzer Beschreibungen zurückgibt, eine für jede verletzte Regel.
Prüfe alle drei Regeln: Der Fragetext darf nicht leer sein, die erwartete Antwort darf nicht leer sein, und points muss größer als null sein. Eine Frage, die zwei Regeln verletzt, soll zwei Beschreibungen ergeben, eine gültige Frage eine leere Liste.
Den genauen Wortlaut jeder Beschreibung wählst du selbst. Führe das Programm aus und lies, was es über die drei Fragen am Ende meldet.