Kapitel 14 · Übung
Abschlussprojekt: Eine gemeinschaftliche Leihbibliothek
Das Problem wählen und die Schnittstellenzusage lesen
Dreizehn Kapitel haben dir die Bausteine gegeben. Dieses Kapitel fordert dich auf, mit allen gemeinsam etwas zu bauen.
Du schreibst eine gemeinschaftliche Leihbibliothek: Sie registriert Mitglieder, katalogisiert ausleihbare Dinge, verleiht sie, nimmt sie zurück, zeigt Verfügbares und findet überfällige Ausleihen. Kein Web, keine Datenbank, keine Dateien, keine Datumsangaben außer einer einfachen Tagesnummer. Alles Interessante daran ist eine Frage des Entwurfs.
Dieses Kapitel beginnt ein neues Projekt. Deine Dateien bleiben innerhalb dieses Kapitels von Lektion zu Lektion erhalten. Bearbeite die genannte Datei im Projektbaum. Run führt immer main.py aus, das bis zur letzten Lektion ein Platzhalter bleibt. Submit prüft den aktuellen Meilenstein im gesamten Projekt. Reset stellt die Kapitelvorlage wieder her und entfernt deine Arbeit; die Häkchen für abgeschlossene Lektionen bleiben erhalten.
Was eine Schnittstellenzusage ist
Du sollst die Schnittstelle nicht erfinden. Sie ist vorgegeben und heißt auch Vertrag, denn sie ist die Zusage, auf die sich der Rest der Welt verlassen darf.
register_member(member)
add_item(item)
checkout(item_id, member_id, checkout_day)
return_item(item_id)
available_items()
active_loans()
overdue_loans(on_day)
Sieben Operationen auf einer Klasse, Library. Alles andere im Projekt dient dazu, diese sieben zum Funktionieren zu bringen.
So kommen echte Aufgaben meistens an. Jemand anderes hat entschieden, was das System tun muss, manchmal weil anderer Code es bereits aufruft. Deine Aufgabe ist, etwas zu bauen, das diese Zusagen einhält. Du hast echte Freiheit, aber sie liegt unterhalb der Schnittstellenzusage: wie du Gegenstände speicherst, wie du Hilfsfunktionen nennst, ob Book und Game eine Basisklasse teilen. Nichts davon ist jemandem zugesagt, also ist nichts davon festgelegt.
Mit der Form beginnen, nicht mit dem Verhalten
Der erste Schritt bei einem Projekt dieser Größe ist nicht, die schwierigste Methode zu schreiben. Er besteht darin, die Form anzulegen.
class Library:
def register_member(self, member):
raise NotImplementedError
NotImplementedError ist Pythons Art zu sagen: Das soll existieren und funktioniert noch nicht. Es ist keine Entschuldigung, sondern ein Platzhalter, der deutlich fehlschlägt. Wenn etwas ihn versehentlich aufruft, merkst du es sofort, statt stillschweigend None zu erhalten.
Eine Datei voller solcher Platzhalter ist mehr wert, als sie aussieht. Sie liefert dir eine ausführbare Checkliste, zwingt dich, die Parameter jeder Methode festzulegen, bevor deren Rumpf dich ablenkt, und sorgt dafür, dass jede spätere Lektion etwas ausfüllt, statt Struktur zu erfinden.
Warum raise NotImplementedError schreiben, statt den Methodenrumpf bei pass zu belassen?
Eine Signatur sorgfältig lesen
Zwei Details der Zusage lassen sich leicht überlesen. Beide sind bewusste Entscheidungen.
checkout(item_id, member_id, checkout_day) erhält Kennungen, keine Objekte. Wer die ID eines Gegenstands hat, muss den Gegenstand nicht selbst halten. So behält die Bibliothek die Kontrolle über ihren Katalog, statt beliebigen übergebenen Objekten zu vertrauen.
overdue_loans(on_day) erhält einen Tag, statt eine Uhr abzufragen. Kapitel 6 begründete das: Eine Funktion, die die Außenwelt nach der Zeit fragt, lässt sich viel schwerer testen als eine, der die Zeit mitgeteilt wird. Aus demselben Grund ist jedes Datum in diesem Projekt eine einfache ganzzahlige Tagesnummer.
Was du jetzt baust
Erstelle Library in library.py mit allen sieben Methoden, die jeweils NotImplementedError auslösen.
Achte auf die richtigen Parameternamen. Spätere Lektionen und die Prüfungen verwenden sie.
Aufgabe
Schreibe die öffentliche Form von Library in library.py.
Definiere eine Klasse, Library, mit einem __init__, das nur self erhält, und diesen sieben Methoden:
register_member(self, member)add_item(self, item)checkout(self, item_id, member_id, checkout_day)return_item(self, item_id)available_items(self)active_loans(self)overdue_loans(self, on_day)
Jeder Methodenrumpf besteht aus raise NotImplementedError. Noch funktioniert nichts, und nichts soll das vortäuschen.
Verwende exakt diese Namen in dieser Reihenfolge mit diesen Parameternamen. Der Rest des Kapitels baut darauf auf.