0%

Hoofdstuk 9 · oefening

Overerving en polymorfisme

Verschillende objecten, dezelfde bewerking

Elke vraag in deze cursus was tot nu toe hetzelfde soort vraag: een vraagtekst die wordt gecontroleerd tegen één opgeslagen tekstantwoord. Echte quizzen werken niet zo.

  • Een tekstvraag vergelijkt wat is getypt.

  • Een meerkeuzevraag accepteert een van meerdere opties via een letter.

  • Een numerieke vraag accepteert alles wat dicht genoeg bij het juiste getal ligt.

Drie verschillende regels om de juistheid te bepalen. Interessant is wat de rest van het programma daarover zou moeten weten.

De numerieke voorbeelden gebruiken één klein ingebouwd hulpmiddel. abs(number) geeft de afstand van het getal tot nul terug, dus abs(-0.05) is 0.05. Die afstand vergelijken met een tolerantie vertelt ons of een numeriek antwoord dichtbij genoeg is.

De versie die alles weet

Dit is de voor de hand liggende eerste poging en de reden dat dit hoofdstuk bestaat:

Try it

Het werkt. Denk nu na over wat er daarna gebeurt.

Er komt een vierde soort, bijvoorbeeld waar of onwaar. Je voegt hier een vertakking toe. Daarna moet de scoreberekening de soort kennen en krijgt die dezelfde keten. Vervolgens de weergave, dan het rapport en dan alles wat vragen uit opslag leest.

De string kind heeft zich door het programma verspreid en elke plek die die leest moet worden gevonden en samen met de andere worden bijgewerkt. Mis je er één, dan gedraagt het nieuwe vraagtype zich ongemerkt als de laatste vertakking, of komt het bij return False uit en is het voor iedereen fout.

Het signaal

Het patroon om te herkennen is een keten van voorwaarden die vragen wat voor soort ding is dit, gevolgd door code die het bijbehorende werk doet:

if kind == "text":
    ...
elif kind == "choice":
    ...
elif kind == "numeric":
    ...

Eén van die ketens is prima. Het probleem is dat ze zich vermenigvuldigen en dat kopieën van dezelfde keten in verschillende functies uit elkaar gaan lopen. Een type toevoegen wordt een archeologische expeditie.

Vergelijk dat met een keten die niet onder dit patroon valt:

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"

Die vraagt naar een waarde, niet naar het soort object, en een cijfercategorie toevoegen verandert één plek. Laat die met rust. Het patroon dat je wilt verwijderen is de herhaalde vertakking op het soort object. Dit staat los van de identiteitsvraag uit hoofdstuk 7: of twee namen naar hetzelfde object verwijzen.

Welke keten is het waarschuwingssignaal dat dit hoofdstuk behandelt?

De stap

De uitweg is het idee dat hoofdstuk 4 introduceerde en dat dit hoofdstuk afmaakt: zet het gedrag op het object.

Try it

Er is één bewust ruw randje: NumericQuestion.is_correct("three") gooit ValueError op. Alle drie de klassen bieden dezelfde methodenaam, maar voldoen voor gewone invoer nog niet aan dezelfde belofte. Les 8 komt terug op die gedragsafspraak en herstelt die.

De lus onderaan is waar het om gaat. Die roept drie keer question.is_correct(response) aan, krijgt drie verschillende regels en bevat helemaal geen if. Die vraagt nooit welk soort vraag die heeft.

Elk object weet wat het is, dus niets anders hoeft dat te weten. Een vierde type toevoegen betekent één klasse schrijven die aan dezelfde bewerkingen voldoet; gebruikers zoals deze lus blijven ongewijzigd werken.

Meerdere verschillende typen via één gedeelde bewerking gebruiken, zonder dat de aanroepende code weet welk type welk is, heet polymorfisme. De naam is langer dan het idee.

Waar dit hoofdstuk echt over gaat

Misschien viel je op dat die drie klassen zijn geschreven zonder de mechanismen waarmee overerving meestal wordt geïntroduceerd. Er is geen basisklasse en er wordt niets gedeeld.

Dat is bewust. Polymorfisme is het doel en overerving is één manier om dat te bereiken, bruikbaar in minder situaties dan de meeste inleidingen suggereren. De volgende twee lessen laten zien hoever je zonder overerving komt, zodat je bij de introductie in les 4 kunt beoordelen of die zijn plek verdient.

Opdracht

Dit programma beslist alles door te vragen welk soort vraag het heeft, op twee afzonderlijke plekken die al uiteen zijn gaan lopen: score_quiz verwerkt drie soorten en describe_all slechts twee.

De drie gevalideerde vraagklassen zijn gegeven. Vervang beide dictionaryketens door polymorfe aanroepen.

Herschrijf daarna score_quiz(questions, responses) en describe_all(questions) zodat geen van beide een if bevat die vraagt welk soort vraag die heeft. Ze moeten op elke vraag dezelfde twee methoden aanroepen en elk object de eigen regel laten leveren.

De puntenwaarden blijven zoals ze zijn. Richt je op de ene nieuwe moeilijkheid: gebruikende code die elk object om dezelfde bewerking vraagt zonder typecontroles.