Hoofdstuk 6 · oefening
Samenwerkende objecten
Geef een object aan een ander object door
Eén regel uit het vorige hoofdstuk is het waard om nog eens te bekijken:
attempt.record(question, response)
Een Attempt ontvangt een volledige Question. Je had het ook zo kunnen schrijven dat het de onderdelen ontvangt:
attempt.record(question.prompt, question.answer, question.points, response)
Beide werken. Het verschil ertussen is het onderwerp van dit hoofdstuk en bepaalt hoeveel van je programma moet veranderen als iets kleins verandert.
Een object bij binnenkomst uit elkaar halen
Hier is een functie report_line die op de tweede manier is geschreven:
Lees de aanroep. Elke aanroeper moet weten dat een vraag precies deze drie onderdelen heeft, in deze volgorde, en moet ze er zelf uithalen. Kijk vervolgens naar de functie-inhoud: correct = response == answer maakt de regel opnieuw die al bij Question.is_correct hoort.
Stel nu dat vragen een optie case_sensitive krijgen. De signatuur van deze functie verandert, elke aanroep verandert en de vergelijkingsregel moet zowel hier als in Question worden bijgewerkt. Die taak hoort helemaal niet bij deze functie.
Het geheel doorgeven
Dezelfde uitvoer. De aanroep is nu report_line(question, "3"), wat leest zoals het bedoeld is, en de vergelijkingsregel wordt opgevraagd in plaats van nagemaakt. Voeg nu case_sensitive toe aan Question en deze functie hoeft helemaal niet te veranderen, omdat die nooit wist hoe de juistheid werd bepaald.
Een vraag krijgt een nieuw attribuut en is_correct gaat dat gebruiken. Welke versie van report_line moet veranderen?
Het signaal om op te letten
Er is een herkenbaar waarschuwingssignaal in de eerste versie: een lange parameterlijst waarvan de argumenten allemaal uit hetzelfde object komen, gevolgd door code die een beslissing opnieuw opbouwt die dat object zelf had kunnen nemen.
Je kunt het bij de aanroep herkennen, zonder de functie ook maar te lezen:
summarize(attempt.learner, attempt.score, attempt.response_count)
Drie argumenten, één bron. Die aanroep vraagt eigenlijk om een Attempt.
Wanneer de onderdelen de juiste keuze zijn
Maak hier geen regel van dat functies altijd objecten moeten ontvangen. Een functie die echt met één waarde werkt, moet die waarde ontvangen:
def shorten(text, limit):
...
shorten heeft niets aan een Question. De functie werkt met tekst. Als je er een vraag aan doorgeeft, wordt die onbruikbaar voor elke andere string in het programma. Vraag je af van welke waarden de taak van de functie werkelijk afhangt. Als die allemaal uit één object komen en de functie beslissingen moet nemen die dat object zelf kan nemen, geef je het object door.
De oefening bevat een functie met de eerste vorm. Geef die de tweede.
Opdracht
Beide functies halen hier een object bij binnenkomst uit elkaar en bouwen daarna beslissingen opnieuw op die het object zelf zou kunnen nemen.
Herschrijf describe_result zodat die (question, response) ontvangt, en summarize_attempt zodat die (attempt) ontvangt. Vraag elk object om wat je nodig hebt in plaats van de onderdelen te ontvangen, en gebruik de methoden die het al biedt in plaats van hun regels te herhalen.
Pas de twee aanroepen onderaan daarop aan. De afgedrukte uitvoer mag niet veranderen: het doel van de oefening is dat de interface verbetert terwijl het gedrag gelijk blijft.