Ein Programm über mehrere Dateien organisieren · Übung
Domäne, Oberfläche und Einstieg in Module trennen
Kapitel 6 erklärte, dass ein Domänenobjekt berechnen und zurückgeben, aber nie ausgeben sollte. Darstellung gehört an den Rand des Programms. Das war eine Regel für Klassen. Im Projekt wird sie zu einer Regel für Dateien und leichter einzuhalten: Eine Dateigrenze ist sichtbarer als eine Methodengrenze.
Die drei Schichten
main.py entry what this program does when you run it
report.py interface facts turned into text
models.py domain the rules, and the vocabulary they use
scoring.py domain rules with no state of their own
Abhängigkeiten zeigen nach unten. main.py importiert aus Domänen- und Berichtsmodulen. report.py verwendet die Bewertungsregeln und darf auch aus models importieren. Nichts in der Domäne importiert jemals etwas aus einer darüberliegenden Schicht.
Diese einseitige Anordnung ist die Richtung der Modulabhängigkeiten. Sie bildet den ganzen Entwurf: Dasselbe models.py kann diesem Terminalprogramm, einer Webseite und einer Testsammlung dienen, ohne deren Existenz zu kennen.
Der Test für die richtige Grenze
Du kannst zu jedem Modul eine Frage stellen, die viel präziser ist als „Fühlt sich das wie die richtige Datei an?“:
Würde sich diese Datei ändern, wenn das Programm von einem Terminal auf eine Webseite umzieht?
models.py: nein. Eine Frage bleibt eine Frage.scoring.py: nein. Neunzig Prozent ergeben in beiden Fällen ein A.report.py: ja. Textzeilen werden zu HTML.main.py: Diese Verbindung der Kommandozeilenteile würde durch den Einstiegscode der Webanwendung ersetzt.
Dateien mit „ja“ als Antwort gehören zur Benutzerschnittstelle. Dateien mit „nein“ zur Domäne. Lautet die Antwort „teilweise“, erledigt die Datei zwei Aufgaben und sollte aufgeteilt werden.
Warum darf models.py niemals aus report.py importieren?
Wo die Grenze praktisch liegt
Die Unterscheidung ist nicht immer offensichtlich. Zwei Fälle kommen ständig vor.
Eine Zahl formatieren. Wenn percentage(4, 10) den Wert 0.4 zurückgibt, ist das Domäne: eine Tatsache. Die Darstellung als "40%" gehört zur Benutzerschnittstelle, denn Prozentzeichen, Nachkommastellen und die Wahl zwischen Komma und Punkt sind Darstellungsentscheidungen.
Eine Bestehensgrenze festlegen. Domäne. Die Bestehensgrenze eines Quiz ist eine Quizregel, keine Aussage über die Anzeige des Ergebnisses. Deshalb stand sie in Kapitel 6 auf Quiz.
Eine hilfreiche Formulierung: Die Domäne entscheidet, was gilt. Die Benutzerschnittstelle entscheidet, wie es ausgedrückt wird.
Eine Schicht ist nicht immer eine Datei
Vier Dateien für ein Programm mit sechzig Zeilen sind fast zu viele. Dieses Projekt liegt genau an der Grenze, ab der sich die Aufteilung lohnt.
Verstehe das Kapitel nicht als Anweisung, für jedes Skript vier Module zu erstellen. Wenn ein Programm so groß wird, dass du es nicht mehr im Kopf behalten kannst, sind dies die passenden Trennlinien. Es sind fast immer dieselben drei.
Echte Projekte wachsen durch mehr Dateien innerhalb jeder Schicht, nicht durch mehr Schichten. Eine größere Version dieses Programms hätte models.py, attempts.py und question_types.py in der Domäne sowie einen Einstieg für jede Art, die Anwendung zu starten.
Die Übung
report_lines zieht aus main.py in ein eigenes report.py. Lektion 7 fasst die Module dann zu einem Paket zusammen, und Lektion 8 verbindet das fertige Projekt.
Aufgabe
Erstelle report.py, die Schnittstellenschicht des Projekts und die Datei im Editor dieser Lektion.
Verschiebe report_lines(attempt) unverändert dorthin und ergänze headline(quiz), das "<title> (<total> points)" zurückgibt.
report.py darf aus scoring importieren, denn es braucht die Regel zur Umwandlung eines Punktestands in eine Note. Es darf nicht aus main importieren: Nichts im Projekt darf vom Einstieg abhängen.
Es darf außerdem nichts ausgeben, weder beim Import noch bei Funktionsaufrufen. Ein Bericht gibt Text zurück und überlässt dem aufrufenden Code dessen Verwendung.
Behalte die aktuelle Kopie vorerst in main.py, damit Run noch funktioniert. Submit prüft report.py. Lektion 8 entfernt die Kopie beim Import des Paketberichts.