0%

Vererbung und Polymorphie · Abschlussprojekt

Abschlussprojekt: Projekt: Mehrere Fragearten unterstützen

Der Kurs begann mit einem Quiz aus gleich aufgebauten Dictionary-Fragen und einer Prüffunktion, die die Regel kannte. Am Ende dieses Kapitels hält ein Quiz vier Fragearten und wird von Code bewertet, der keine davon kennt.

Was du baust

Question          the base: prompt, points, optional collaborators
  TextQuestion    matches ignoring spaces and case
  ChoiceQuestion  matches one option
  NumericQuestion matches within a tolerance
  SurveyQuestion  ungraded, always correct

TimeLimit         a thing a question has
Hint              a thing a question has

Quiz              holds questions of any kind
ScoreReport       turns an attempt into lines

Vier Unterklassen, weil jede eine tatsächlich andere Art ist, eine Ebene tief. Zwei Partnerklassen, weil sich Zeitbegrenzung und Hinweise mit allen vier frei kombinieren lassen.

Die Zusage jeder Unterklasse

is_correct(response) nimmt einen beliebigen String entgegen und gibt True oder False zurück. Es löst niemals eine Ausnahme aus und gibt niemals etwas anderes zurück.

Wegen dieses Satzes lassen sich Quiz und ScoreReport einmal schreiben. Die Bewertung prüft ihn für jede Unterklasse mit einer leeren und einer unsinnigen Antwort. Das ist die einfache Version des Familientests, den Kapitel 13 ausführlich aufbaut.

SurveyQuestion wird nicht bewertet und ist immer richtig. Warum ist es eine Unterklasse statt einer eigenen, nicht verwandten Klasse?

Regeln, die die Bewertung prüft

  • Keine Funktion außerhalb der Klassen verwendet isinstance oder nennt eine Unterklasse.

  • Jede Unterklasse gibt aus is_correct für einen beliebigen String einen booleschen Wert zurück.

  • describe() wird einmal geschrieben, in der Basis.

  • Keine Klasse verwendet eine Ausgabe.

  • Eine fünfte Frageart, die die Bewertung erfindet, funktioniert überall ohne Änderung.

Der letzte Punkt ist der eigentliche Test dieses Kapitels. Wird eine später geschriebene Klasse von jemandem, der deinen Code nie gesehen hat, korrekt bewertet und dargestellt, erfüllt der Entwurf seine Zusage.

Fertigstellen

Lies dein fertiges Quiz und ScoreReport und zähle, wie oft sie eine bestimmte Frageart erwähnen. Die Antwort sollte null sein.

Erfinde dann eine fünfte Art. Eine Wahr-oder-falsch-Frage, eine Schlüsselwortfrage mit mehreren akzeptierten Schreibweisen oder eine Bereichsfrage eignen sich. Füge sie zum Quiz hinzu, ohne Quiz oder ScoreReport zu bearbeiten. Wenn dieses neue Verhalten durch das unveränderte Programm läuft, hast du das Projektziel erreicht.

Aufgabe

Die validierten Partnerklassen und die Basisklasse sind vorgegeben. Baue die konkrete Familie und die beiden Klassen, die sie ohne Kenntnis konkreter Typen verwenden.

Question(prompt, points, timing=None, hint=None) hält den gemeinsamen Zustand. describe() gibt "<prompt> (<points> points)" zurück, gefolgt von der Beschreibung jedes vorhandenen Partners, Zeitbegrenzung vor Hinweis. is_correct(response) löst NotImplementedError aus. Ergänze eine Property kind_label, die "question" zurückgibt.

Die vier Unterklassen überschreiben jeweils is_correct und kind_label. Keine definiert describe neu:

  • TextQuestion(prompt, points, answer, ...), Bezeichnung "text", vergleicht ohne Leerraum an den Rändern und ohne Beachtung der Groß- und Kleinschreibung.

  • ChoiceQuestion(prompt, points, correct_option, ...), Bezeichnung "multiple choice", akzeptiert die Option in beiden Schreibweisen.

  • NumericQuestion(prompt, points, answer, tolerance, ...), Bezeichnung "numeric", vergleicht innerhalb der Toleranz und gibt False zurück, statt eine Ausnahme auszulösen, wenn die Antwort keine Zahl ist.

  • SurveyQuestion(prompt, ...), Bezeichnung "survey", ist 0 Punkte wert und immer richtig.

TimeLimit(seconds) und Hint(text) sind vorgegeben und validiert. Behalte auch die bisherigen Konstruktionsregeln: Erwartete Textantworten und Auswahloptionen dürfen nicht leer oder nur Leerraum sein, die numerische Toleranz darf nicht negativ sein, und ein Quiztitel darf nicht leer sein. Lehne diese ungültigen Werte mit ValueError ab.

Quiz(title) hält Fragen, gibt sie sicher heraus und meldet question_count und total_points. ScoreReport(quiz, responses) gibt mit lines() pro Frage eine Zeile "<description>: correct" oder ": incorrect" zurück, dann "Score: <earned> of <total>" und dann "Kinds: <labels, comma separated, in order, without repeats>".

Quiz.add(None) löst ValueError aus. Ein Bericht muss genau eine Antwort pro Quizfrage erhalten. lines() löst sowohl bei zu wenigen als auch bei zu vielen ValueError aus, damit zip keine Daten unbemerkt verwirft.

Jedes is_correct muss für einen beliebigen String True oder False zurückgeben und darf niemals eine Ausnahme auslösen. Keine Klasse verwendet eine Ausgabe, und nichts außerhalb der Familie nennt eine Unterklasse.