Hoofdstuk 5 · oefening
De geldige toestand van objecten bewaken
Bepaal de geldige toestand van een object
De klasse Question uit hoofdstuk 4 doet haar werk. Ze bewaart een vraagtekst naast het antwoord en bepaalt zelf of een ingevoerd antwoord goed is:
Kijk nu wat die klasse nog meer accepteert:
Een vraag zonder vraagtekst en zonder antwoord. Python maakt die zonder bezwaar en is_correct("") meldt vrolijk True. Elke cursist die een leeg antwoord indient, heeft het goed.
De klasse bracht de gegevens bij elkaar. Ze beloofde niet dat die gegevens ook zinvol waren.
Regels die altijd waar moeten zijn
Bedenk wat er moet gelden om een object van Question de moeite waard te maken:
de vraagtekst is niet leeg, omdat een cursist iets moet lezen;
het antwoord is niet leeg, omdat anders een leeg ingevoerd antwoord als goed telt;
de toegekende punten zijn positief, omdat deze quiz punten geeft voor elk goed antwoord. Andere quizontwerpen kunnen bewust vragen zonder punten bevatten.
Zulke regels hebben een naam. Een regel die voor een object waar moet zijn vanaf het moment dat het wordt gemaakt tot het moment dat het wordt weggegooid, heet een invariant: het deel dat niet varieert.
Het woord is minder belangrijk dan de gewoonte. Vraag bij het ontwerpen van een klasse wat er altijd voor moet gelden. Hoofdstuk 4 vroeg wat een object moet bevatten en wat het moet doen. Dit hoofdstuk voegt een derde vraag toe: wat mag het nooit worden?
Welke uitspraak beschrijft een invariant van een object van Question?
Niets houdt toezicht
Hier komt het ongemakkelijke deel. Zelfs een correct gemaakte Question kan achteraf worden beschadigd:
Deze versie van Question definieert geen controles voor latere attribuuttoewijzingen. Het object was zojuist geldig en is nu kapot, en nergens werd een probleem gemeld.
Dit is geen gebrek van Python. Gewone Python-attributen accepteren een nieuwe waarde wanneer code die toewijst. Wat we gaan toevoegen is een bewuste beperking, gekozen door de schrijver van de klasse, op plekken waar een verkeerde waarde later echte problemen zou veroorzaken.
Waar het probleem werkelijk verschijnt
De schade hierboven ontstaat stilletjes. Dat is precies het probleem. Een kapotte Question faalt niet waar ze beschadigd raakte. Ze faalt ergens anders, later, in code die alle reden had haar te vertrouwen:
for question in question_list:
print(question.prompt.upper())
Als één vraagtekst None is, veroorzaakt deze lus een AttributeError en wijst de traceback hiernaartoe. Maar deze lus is onschuldig. De fout ontstond waar die None werd toegewezen, mogelijk in een andere functie, geschreven door iemand anders op een andere dag.
De rest van dit hoofdstuk dicht dat gat:
Les 2 weigert om überhaupt een ongeldig object te bouwen.
Les 3 vervangt losse attribuuttoewijzingen door methoden met betekenis.
Lessen 4 en 5 scheiden wat een klasse belooft van de manier waarop ze dingen opslaat.
Les 6 staat één zelfstandig geldige wijziging toe via een gecontroleerde setter.
Les 7 zorgt dat een wijziging aan samenhangende attributen volledig plaatsvindt of helemaal niet.
Benoem de regels voordat je ze afdwingt
Voordat je nieuwe syntaxis schrijft, helpt het om de regels helder te formuleren. De oefeneditor bevat een klasse Question met een toegevoegd attribuut points en een functie problems(question) die een lijst met beschrijvingen moet teruggeven van alles wat er mis is met een vraag.
Die functie gebruikt niets nieuws. Het zijn gewone if-instructies op attributen die je al kunt lezen. Door die te schrijven, ontdek je of de regels in je eigen hoofd duidelijk zijn. Dat is de enige nuttige voorbereiding om de klasse ze te laten afdwingen.
Opdracht
Maak problems af zodat de functie een lijst met korte beschrijvingen teruggeeft, één voor elke regel die de vraag schendt.
Controleer alle drie de regels: de vraagtekst mag niet leeg zijn, het antwoord mag niet leeg zijn en points moet groter zijn dan nul. Een vraag die twee regels schendt, moet twee beschrijvingen opleveren. Een goede vraag moet een lege lijst opleveren.
Je mag de precieze formulering van elke beschrijving zelf kiezen. Voer het programma uit en lees wat het meldt over de drie vragen onderaan.