Zusammenarbeit zwischen Objekten · Übung
Objektbeziehungen lesen und zeichnen
Du hast jetzt fünf Klassen geschrieben, die zusammenarbeiten. Wer neu zum Programm kommt, müsste alle fünf lesen, um ihre Zusammenarbeit zu verstehen. Es geht schneller. Die letzte Fähigkeit dieses Kapitels ist, einen Entwurf zu beschreiben, ohne ihn zu programmieren.
Ein Entwurf in fünf Zeilen
Die Schreibweise aus Lektion 3 reicht für die meisten Gespräche:
a Quiz has many Questions
a QuizAttempt has a Quiz
a QuizAttempt has many Responses
a Response has a Question
a ScoreReport has a QuizAttempt
Lies sie als Karte, die zeigt, wer wen fragen kann. Ein ScoreReport erreicht einen QuizAttempt und darüber ein Quiz. Ein Question erreicht dagegen nichts anderes. Fragen stehen ganz unten: Sie antworten und fragen nie.
Diese letzte Beobachtung ist wertvoller, als sie aussieht. Objekte ohne ausgehende Pfeile kannst du für sich testen, überall wiederverwenden und mit Zuversicht ändern. Gibt es solche Objekte in einem Entwurf nicht, hängt alles von allem ab.
Derselbe Entwurf als Bild
Bei mehr als einer Handvoll Klassen lässt sich eine Skizze schneller lesen:

Pfeile zeigen vom haltenden Objekt auf das gehaltene. Das ist keine formale Notation und muss es auch nicht sein. Die Skizze soll auf einen Bildschirm passen und Zusammenhänge sichtbar machen, etwa: Zwei Pfeile zeigen auf Question, keiner von ihm weg, und es gibt keinen Weg von einem Question zurück zum Quiz, das es hält.
Worauf du in einer Zeichnung achten solltest
Wenn ein Entwurf gezeichnet ist, werden manche Probleme sichtbar, ohne Code zu lesen.
Pfeile in beide Richtungen. Zeigt Quiz auf QuizAttempt und QuizAttempt zurück auf Quiz, frage, ob beide Richtungen nötig sind. Die Beziehung kann Änderungen beider Klassen aneinanderkoppeln. Objektreferenzen erfordern nicht, dass beide Klassen einander importieren. Unnötige Abhängigkeiten erschweren aber später die Aufteilung in Module. Kapitel 10 betrachtet Importe.
Ein Objekt mit Pfeilen zu allem. Das ist das allzuständige Objekt aus Lektion 5, als Form sichtbar.
Eine lange Kette. ScoreReport -> QuizAttempt -> Quiz -> Question ist in Ordnung, solange jeder Schritt nur seinen Nachbarn fragt. Problematisch wird es, wenn der Bericht bis ganz nach unten greift. Im Code zeigt sich das als report.attempt.quiz.questions()[0].points.
Ein Objekt, auf das nichts zeigt. Prüfe, wer es erzeugt und verwendet. Vielleicht ist es ein Einstiegspunkt, ein nicht eingezeichneter Aufrufer verwendet es, oder es ist überflüssig.
Ein Entwurf zeigt einen Pfeil von Quiz zu QuizAttempt und einen weiteren zurück von QuizAttempt zu Quiz. Worauf deutet das hin?
Vor dem Schreiben zeichnen
Am nützlichsten ist das, bevor überhaupt eine Klasse existiert.
Nimm die Anforderung „Eine lernende Person kann ein Quiz wiederholen, und wir behalten jeden Versuch“. Schreibe die Hauptwörter und dann die Beziehungen auf:
a Learner has many QuizAttempts
a QuizAttempt has a Quiz
a QuizAttempt has a started_on day
Drei Zeilen, und schon steht eine Entscheidung fest: Versuche gehören zu einer lernenden Person statt zu einem Quiz. Das hat Folgen. „Zeige mir alles, was Mina gemacht hat“ ist nun einfach, „Zeige mir alle, die die Abschlussprüfung abgelegt haben“ erfordert eine Suche. Das auf Papier zu bemerken kostet eine Minute. Es nach vier geschriebenen Klassen zu bemerken kostet einen Nachmittag.
Die Faustregel: Skizziere bei mehr als ungefähr drei Klassen oder immer dann, wenn du unsicher bist, welches Objekt etwas halten sollte.
Die Übung
Du erhältst einen beschriebenen Entwurf und teilweise fertige Klassen. Passe den Code an den Entwurf an und nutze ihn anschließend, um eine Frage zu einer noch nicht umgesetzten Änderung zu beantworten.
Aufgabe
Vervollständige Learner, sodass der Code diesem Entwurf entspricht:
a Learner has many QuizAttempts
a QuizAttempt has a Quiz
a QuizAttempt has many Responses
a Response has a Question
a Quiz has many Questions
Question, Quiz, Response und QuizAttempt sind vorgegebene Partnerklassen. Learner(name) hält seinen öffentlichen Namen. start(quiz) erzeugt einen neuen QuizAttempt für dieses Quiz, merkt ihn sich und gibt ihn zurück. attempt_count meldet die Anzahl gestarteter Versuche. best_score meldet den höchsten aktuellen Punktestand dieser Versuche oder 0, wenn es keine gibt. history() gibt eine neue Liste der Versuche in ihrer Startreihenfolge zurück. Änderungen an dieser Liste dürfen den Verlauf der lernenden Person nicht verändern.
Vergleiche die vorgegebenen Klassen mit den Pfeilen, bevor du Learner implementierst. So bleibt die Übung beim Lesen einer Zeichnung von Objektbeziehungen, statt vier frühere Lektionen auf einmal nachzubauen.
Folge den Pfeilen. Nichts zeigt von einem Quiz zurück zu einem QuizAttempt. Ein Quiz darf daher niemals Versuche oder Lernende erwähnen. Von einem Question zeigt nichts irgendwohin. Eine Frage antwortet also weiterhin und fragt nie.
Das Programm gibt einen kurzen Verlauf für eine Person mit zwei Versuchen desselben Quiz aus.