Een programma over bestanden verdelen · oefening
Scheid modules voor domein, interface en startpunt
Hoofdstuk 6 stelde dat een domeinobject hoort te berekenen en terug te geven, nooit af te drukken, en dat presentatie aan de rand van het programma hoort. Dat was een regel over klassen. In een project wordt het een regel over bestanden en die is makkelijker te volgen, omdat een bestandsgrens zichtbaar is op een manier waarop een methodegrens dat niet is.
De drie lagen
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
Afhankelijkheden wijzen naar beneden. main.py importeert uit de domein- en rapportmodules. report.py gebruikt de scoreregels en kan ook uit models importeren. Niets in het domein importeert ooit iets erboven.
Deze opzet in één richting is de richting van moduleafhankelijkheden. Dat is het hele ontwerp: dezelfde models.py kan dit terminalprogramma, een webpagina en een testsuite bedienen zonder te weten dat een ervan bestaat.
De toets die het ontwerp controleert
Er is een vraag die je bij elke module kunt stellen en die veel scherper is dan ‘voelt dit als het juiste bestand?’:
Als het programma van een terminal naar een webpagina verhuisde, zou dit bestand dan veranderen?
models.py: nee. Een vraag is een vraag.scoring.py: nee. Negentig procent is hoe dan ook een A.report.py: ja. Tekstregels worden HTML.main.py: deze verbindingen voor de opdrachtregel zouden worden vervangen door de startcode van de webapplicatie.
Bestanden die ‘ja’ antwoorden zijn interface. Bestanden die ‘nee’ antwoorden zijn domein. Als een bestand ‘gedeeltelijk’ antwoordt, doet het twee taken en vraagt het om splitsing.
Waarom mag models.py nooit uit report.py importeren?
Waar de grens in de praktijk ligt
Het onderscheid is niet altijd duidelijk en twee gevallen komen voortdurend terug.
Een getal opmaken. percentage(4, 10) dat 0.4 teruggeeft is domein: het is een gegeven. Dat weergeven als "40%" is interface, omdat de keuze voor een procentteken, decimalen en een komma of punt presentatie is.
Een slaaggrens bepalen. Domein. De slaaggrens van een quiz is een regel over quizzen, niet over hoe het resultaat wordt getoond. Daarom stond die in hoofdstuk 6 op Quiz.
De nuttige formulering: het domein bepaalt wat waar is, de interface bepaalt hoe je het zegt.
Een laag is niet altijd een bestand
Vier bestanden voor een programma van zestig regels is bijna te veel en dit project zit precies op de grens waar splitsen begint te lonen.
Lees dit hoofdstuk niet als een opdracht om voor elk script vier modules te maken. Lees het zo: als een programma zo groeit dat je het niet meer in je hoofd kunt houden, zijn dit de grenzen waarlangs je splitst, en het zijn bijna altijd dezelfde drie.
Echte projecten krijgen meer bestanden binnen elke laag, niet meer lagen. Een grotere versie van dit programma zou models.py, attempts.py en question_types.py in het domein hebben, met een startpunt voor elke manier waarop de applicatie wordt gestart.
De oefening
report_lines verhuist uit main.py naar een eigen report.py. Les 7 groepeert de modules daarna in een package en les 8 verbindt het voltooide project.
Opdracht
Maak report.py, de interfacelaag van het project en het bestand dat bij de editor van deze les hoort.
Verplaats report_lines(attempt) er ongewijzigd naartoe en voeg headline(quiz) toe, dat "<title> (<total> points)" teruggeeft.
report.py mag uit scoring importeren, omdat een score omzetten in een letterbeoordeling een benodigde regel is. Het mag niet uit main importeren: niets in het project mag van het startpunt afhangen.
Het mag ook niets afdrukken, niet tijdens het importeren en niet wanneer de functies worden aangeroepen. Een rapport geeft tekst terug en laat de aanroeper beslissen wat ermee gebeurt.
Behoud de huidige kopie voorlopig in main.py, zodat Run blijft werken. Submit controleert report.py; les 8 verwijdert die kopie wanneer die het rapport uit het package importeert.