Zusammenarbeit zwischen Objekten · Übung
Verantwortlichkeiten zuordnen
Jede Methode, die du schreibst, muss in einer Klasse stehen. Oft sehen zwei oder drei Klassen wie ein vernünftiger Platz aus. Eine schlechte Wahl beschädigt das Programm nicht. Sie erschwert die nächste Änderung, zunächst unsichtbar, bis du sie tatsächlich vornehmen willst.
Hier ist eine konkrete Entscheidung. Ein Quiz soll melden können, ob eine lernende Person bestanden hat, nach einer Regel wie „mindestens 60 Prozent der verfügbaren Punkte“.
Wohin gehört passed?
Drei plausible Plätze
Zu Quiz: quiz.passed(attempt). Das Quiz kennt die verfügbaren Gesamtpunkte, die die Regel benötigt.
Zu QuizAttempt: attempt.passed. Der Versuch kennt den Punktestand, den die Regel ebenfalls benötigt.
Zu ScoreReport: report.passed(). Berichte zu liefern ist die Aufgabe eines Berichts.
Alle drei würden funktionieren. Prüfe jeden Platz mit einer Frage, die nichts mit Geschmack zu tun hat: Welches Objekt müsste sich ändern, wenn sich die Regel ändert?
Die Bestehensgrenze gehört zum Quiz. Ein Übungsquiz könnte 50 Prozent verlangen, eine Abschlussprüfung 80. Sie gehört nicht zu einem einzelnen Versuch und erst recht nicht zu einem Bericht, der ein Ergebnis anzeigen können sollte, ohne selbst darüber zu urteilen.
Der Schwellenwert gehört also zu Quiz. Der Vergleich braucht aber einen Punktestand, und der gehört zu einem Versuch.
Eine Regel auf die zuständigen Objekte verteilen
Zwei kleine Methoden statt einer. Quiz.is_passing(score) verantwortet den Schwellenwert und die Berechnung. QuizAttempt.passed weiß, welchen Punktestand es übergeben muss.
Aufrufender Code erhält den gewünschten Lesezugriff attempt.passed, und die Bestehensgrenze lässt sich durch Änderung einer einzigen Klasse auf 0.8 setzen.
Beachte, dass is_passing einen Punktestand statt eines Versuchs erhält. Ein Quiz muss nicht wissen, dass Versuche existieren. Bleibt das so, könnte dasselbe Quiz auch ein gespeichertes Ergebnis, einen angenommenen Punktestand oder einen Probelauf bewerten.
Eine neue Anforderung lautet: Ein als Abschlussprüfung gekennzeichnetes Quiz verlangt 80 Prozent zum Bestehen, andere verlangen 60. Welche Klasse ändert sich?
Fragen, die eine Methode einordnen
Wenn du unsicher bist, helfen meistens diese Fragen:
Welches Objekt hält die Daten, die die Regel braucht? Hat eines alle, beginne dort.
Welches Objekt würde sich ändern, wenn sich die Regel ändert? Die Regel gehört zu dem Ding, über das sie eine Aussage macht.
Wie würde die Methode heißen? Muss ein guter Name eine andere Klasse erwähnen (
calculate_attempt_score_for_quiz), steht sie wahrscheinlich am falschen Ort.Muss sie hier durch zwei Objekte hindurchgreifen? Dann sollte sie einen Schritt näher an die Daten rücken.
Das allzuständige Objekt
Der Fehler, vor dem dieses Kapitel schützt, hat einen Namen. Ein God Object, ein allzuständiges Objekt, ist eine Klasse, die am Ende alles verantwortet. Meist war sie die erste geschriebene Klasse, und jede neue Funktion ließ sich dort am einfachsten ergänzen.
Es entsteht durch eine vernünftige Entscheidung nach der anderen. Quiz hält die Fragen, also kommt die Punktewertung dorthin. Es hat die Punktestände, also kommen die Berichte dazu. Es hat den Bericht, also auch die Formatierung. Kein einzelner Schritt wirkt offensichtlich falsch, und irgendwann ist Quiz neunhundert Zeilen lang und jede Änderung betrifft diese Klasse.
Merke dir die Anzeichen: eine Klasse, die viel größer ist als ihre Nachbarn; eine Klasse, die überall importiert werden muss; eine Klasse, deren Name nicht mehr beschreibt, was sie tut; und Methoden, die drei voneinander unabhängige Zustandsbereiche benötigen.
Die Übung gibt dir genau diese Situation, in einer Größe, die du in wenigen Minuten korrigieren kannst.
Aufgabe
Quiz ist zu einem allzuständigen Objekt geworden. Es hält die Fragen und hat außerdem Punktewertung, Bestehensregel und Berichtsformatierung angesammelt.
Verschiebe jede Verantwortlichkeit in die passende Klasse:
Quizbehält die Fragen,total_pointsund die Bestehensregel alsis_passing(score). Seinpass_markbleibt ein Konstruktorargument.QuizAttempterhältanswer(question, given),scoreundpassed, das sein Quiz fragt.ScoreReporterhältsummary()und gibt"<learner>: <score> of <total>, <passed or failed>"zurück.
Lösche jede Methode aus Quiz, sobald ihre Aufgabe umgezogen ist. Am Ende sollte Quiz nichts enthalten, das eine lernende Person oder einen Versuch erwähnt.
Das aufrufende Programm im Startcode verwendet bereits die neue Schnittstelle und läuft daher erst, wenn du diese Methoden ergänzt hast. Danach sollte es Folgendes ausgeben:
2 False
Mina: 2 of 5, failed