20. Anwendungsübung: Version 5

Wir werden drei Anwendungen entwickeln:
- Anwendung 1 initialisiert die Datenbank, die die Datei [admindata.json] aus Version 4 ersetzen wird;
- Anwendung 2 berechnet Steuern im Batch-Modus;
- Anwendung 3 berechnet Steuern im interaktiven Modus;
20.1. Anwendung 1: Datenbankinitialisierung
Anwendung 1 wird die folgende Architektur aufweisen:

Dies ist eine Weiterentwicklung der Architektur von Version 4 (siehe Abschnitt |Version 4|): Steuerdaten werden in einer Datenbank statt in einer JSON-Datei gespeichert. Die [DAO]-Schicht wird aktualisiert, um diese Änderung zu implementieren.
20.1.1. Die Datei [admindata.json]

Die Datei [admindata.json] ist dieselbe wie in Version 4:
{
"limites": [9964, 27519, 73779, 156244, 0],
"coeffr": [0, 0.14, 0.3, 0.41, 0.45],
"coeffn": [0, 1394.96, 5798, 13913.69, 20163.45],
"plafond_qf_demi_part": 1551,
"plafond_revenus_celibataire_pour_reduction": 21037,
"plafond_revenus_couple_pour_reduction": 42074,
"valeur_reduc_demi_part": 3797,
"plafond_decote_celibataire": 1196,
"plafond_decote_couple": 1970,
"plafond_impot_couple_pour_decote": 2627,
"plafond_impot_celibataire_pour_decote": 1595,
"abattement_dixpourcent_max": 12502,
"abattement_dixpourcent_min": 437
}
Wir werden die Schlüssel aus diesem Wörterbuch als Spalten in der Datenbank verwenden.
20.1.2. Erstellen der Datenbanken
Wie im Abschnitt |Erstellen einer MySQL-Datenbank| gezeigt, erstellen wir eine MySQL-Datenbank mit dem Namen [dbimpots-2019], die dem Benutzer [admimpots] mit dem Passwort [mdpimpots] gehört. In [phpMyAdmin] sieht dies wie folgt aus:

Ebenso erstellen wir, wie im Abschnitt |Erstellen einer PostgreSQL-Datenbank| gezeigt, eine PostgreSQL-Datenbank mit dem Namen [dbimpots-2019], deren Eigentümer der Benutzer [admimpots] ist und deren Passwort [mdpimpots] lautet. In [pgAdmin] sieht dies wie folgt aus:

Die Datenbanken wurden erstellt, enthalten aber vorerst keine Tabellen. Diese werden vom [sqlalchemy]-ORM erstellt.
20.1.3. Von [sqlalchemy] zugeordnete Entitäten
Wir werden zwei Tabellen erstellen, um die Daten aus [admindata.json] zu kapseln:
Die von [sqlalchemy] definierte Tabelle [tbtranches] sammelt Daten aus den Arrays [limites, coeffr, coeffn] im Wörterbuch [admindata.json]:
# la table des tranches de l'impôt
tranches_table = Table("tbtranches", metadata,
Column('id', Integer, primary_key=True),
Column('limite', Float, nullable=False),
Column('coeffr', Float, nullable=False),
Column('coeffn', Float, nullable=False)
)
Die von [sqlalchemy] definierte Tabelle [tbconstantes] enthält die Konstanten aus dem Wörterbuch [admindata.json]:
# la table des constantes
constantes_table = Table("tbconstantes", metadata,
Column('id', Integer, primary_key=True),
Column('plafond_qf_demi_part', Float, nullable=False),
Column('plafond_revenus_celibataire_pour_reduction', Float, nullable=False),
Column('plafond_revenus_couple_pour_reduction', Float, nullable=False),
Column('valeur_reduc_demi_part', Float, nullable=False),
Column('plafond_decote_celibataire', Float, nullable=False),
Column('plafond_decote_couple', Float, nullable=False),
Column('plafond_impot_celibataire_pour_decote', Float, nullable=False),
Column('plafond_impot_couple_pour_decote', Float, nullable=False),
Column('abattement_dixpourcent_max', Float, nullable=False),
Column('abattement_dixpourcent_min', Float, nullable=False)
)
Die Entitäten, die diesen beiden Tabellen zugeordnet werden, sind wie folgt:

Die Entität [Constants] kapselt die Konstanten aus dem Wörterbuch [admindata.json]:
- Zeile 5: Die Klasse [Constants] erweitert die Klasse [BaseEntity];
- Zeile 7: Über das [sqlalchemy]-Mapping erhält die Klasse [Constante] die Eigenschaft [_sa_instance_state]. Wir schließen sie aus dem [asdict]-Wörterbuch der Entität aus;
- Zeilen 11–23: die Eigenschaften der Entität. Wir haben die im [admindata.json]-Wörterbuch verwendeten Namen wiederverwendet, um das Schreiben des Codes zu vereinfachen;
Die Entität [Tranche] kapselt eine Zeile aus den drei Arrays [limites, coeffr, coeffn] im Wörterbuch [admindata.json]:
- Zeile 5: Die Klasse [Tranche] erweitert die Klasse [BaseEntity];
- Zeile 7: Die von [sqlalchemy] hinzugefügte Eigenschaft [_sa_instance_state] wird aus dem [asdict]-Wörterbuch der Entität ausgeschlossen;
- Zeilen 10–12: die Eigenschaften der Klasse;
Die Zuordnung zwischen den Entitäten [Constants, Slice] und den Tabellen [constants, slices] sieht wie folgt aus:

- Die Zuordnungen sind in den Zeilen 24–29 definiert. Wir haben die Zuordnungen zwischen den Eigenschaften der zugeordneten Entitäten und den Datenbanktabellen weggelassen. Dies ist möglich, wenn die Namen der Tabellenspalten mit denen der Eigenschaften übereinstimmen, denen sie zugeordnet werden sollen. Aus diesem Grund haben wir die Namen der Eigenschaften der zugeordneten Entitäten in die Tabellen aufgenommen. Dies erleichtert das Schreiben und Verstehen des Codes;
20.1.4. Die [sqlalchemy]-Konfigurationsdatei

Wir haben soeben einen Teil der [sqlalchemy]-Konfiguration beschrieben. Die vollständige [config_database]-Datei lautet wie folgt:
- Zeile 1: Die Funktion [configure] erhält ein Wörterbuch als Parameter, dessen Schlüssel [dbms] angibt, welches DBMS verwendet werden soll: MySQL (mysql) oder PostgreSQL (pgres);
- Zeilen 6–12: Die durch die Konfiguration angegebene Datenbank wird ausgewählt;
- Zeilen 14–44: Entitäts-/Tabellenzuordnungen. Diese Zuordnungen sind einfach, da zwischen den Tabellen [tranches] und [constantes] keine Beziehung besteht. Sie sind unabhängig voneinander. Daher müssen keine Fremdschlüssel zwischen ihnen verwaltet werden;
- Zeilen 46–51: Erstellen der Arbeitssitzung [session] der Anwendung;
- Zeilen 53–58: Die relevanten Informationen werden in das Konfigurationswörterbuch geschrieben, das anschließend zurückgegeben wird;
20.1.5. Die [dao]-Schicht
Kehren wir zur Architektur der zu erstellenden Anwendung 1 zurück:

Die [dao]-Schicht [1] muss die Datei [admindata.json] [2] lesen und deren Inhalt an eine der Datenbanken [3, 4] übertragen;

Die [dao]-Schicht stellt die Schnittstelle [1] bereit und wird durch die Klasse [2] implementiert.
Die Schnittstelle [InterfaceDao4TransferAdminData2Database] sieht wie folgt aus:
- Zeilen 8–10: Die Schnittstelle definiert nur eine Methode [transfer_admindata_in_database] ohne Parameter. Da diese Methode Parameter benötigt (welche Datei?, welche Datenbank?), bedeutet dies, dass diese Parameter an den Konstruktor der Klassen übergeben werden, die diese Schnittstelle implementieren;
Die Klasse [DaoTransferAdminDataFromJsonFile2Database] implementiert die Schnittstelle [InterfaceDao4TransferAdminData2Database] wie folgt:
- Zeile 13: Die Klasse [DaoTransferAdminDataFromJsonFile2Database] implementiert die Schnittstelle [InterfaceDao4TransferAdminData2Database];
- Zeilen 15–17: Der Klassenkonstruktor nimmt das Konfigurationswörterbuch als Parameter entgegen. Die folgenden Schlüssel werden verwendet:
- [admindataFilename] (Zeile 27): der Name der JSON-Datei, die die in die Datenbank zu übertragenden Steuerverwaltungsdaten enthält;
- [database] Zeile 32: die [sqlalchemy]-Konfiguration der Anwendung;
- Zeilen 34–37: Löschen der Tabellen [constants] und [brackets], falls vorhanden;
- Zeilen 39–40: Neuerstellung der beiden Tabellen;
- Zeile 43: Abrufen der [sqlalchemy]-Sitzung aus der Konfiguration;
- Zeilen 45–51: Die Arrays [limits, coeffr, coeffn] aus dem Wörterbuch [admindata] werden der Sitzung hinzugefügt. Dazu werden Instanzen der Entität [Tranche] zur Sitzung hinzugefügt;
- Zeilen 52–64: Eine Instanz der Entität [Constantes] wird der Sitzung hinzugefügt;
- Zeilen 66–67: Die Sitzung wird validiert. Wenn die Sitzungsdaten noch nicht in der Datenbank vorhanden waren, werden sie an dieser Stelle eingefügt;
- Zeilen 68–70: Fehlerbehandlung;
- Zeilen 71–74: Die Sitzung wird geschlossen. Dies ist möglich, da die [dao]-Schicht nur einmal verwendet wird;
20.1.6. Anwendungskonfiguration

Die Anwendung wird durch drei Dateien konfiguriert [1]:
- [config] ist die allgemeine Konfigurationsdatei. Sie konfiguriert die [main]-Anwendung. Dabei wird sie von den beiden anderen Dateien unterstützt:
- [config_database], die wir bereits betrachtet haben und die das ORM [sqlalchemy] konfiguriert;
- [config_layers], die die Anwendungsschichten konfiguriert;
Die [config]-Datei sieht wie folgt aus:
- Zeilen 8–36: Erstellen Sie den Python-Pfad der Anwendung;
- Zeilen 38–43: Fügen Sie den Pfad zur Datei [admindata.json] zur Konfiguration hinzu;
- Zeilen 45–48: [SQLAlchemy]-Konfiguration;
- Zeilen 50–53: Instanziierung der Anwendungsschichten;
- Zeile 56: Rückgabe der allgemeinen Konfiguration;
Die Datei [config_layers] sieht wie folgt aus:
- Zeilen 3–4: Instanziierung der [dao]-Schicht. Wir haben gesehen, dass der Konstruktor der Klasse [DaoTransferAdminDataFromJsonFile2Database] das allgemeine Konfigurationswörterbuch der Anwendung als Parameter erwartet;
- Zeile 4: Der Verweis auf die [dao]-Schicht wird der Konfiguration hinzugefügt;
- Zeile 7: Rückgabe der Konfiguration;
20.1.7. Das [main]-Skript der Anwendung


Das Hauptskript [main] lautet wie folgt:
- Zeilen 1–10: Wir warten auf einen Parameter. Wir prüfen, ob er vorhanden und korrekt ist;
- Zeilen 12–14: Wir konfigurieren die Anwendung (allgemein, SQLAlchemy, Layers), indem wir den gewählten DBMS-Typ als Parameter übergeben;
- Zeilen 19–20: Wir benötigen die [dao]-Schicht. Wir rufen sie ab;
- Zeile 25: Wir führen die Übertragung in die Datenbank durch. Alle von der Methode [transfer_admindata_in_database] benötigten Informationen sind in den Eigenschaften der [dao]-Schicht aus Zeile 20 verfügbar. Dort werden sie abgerufen;
Nach Ausführung des Skripts mit der MySQL-Datenbank enthält es die folgenden Elemente (phpMyAdmin):



Spalte [3] zeigt die Werte, die MySQL dem Primärschlüssel [id] zugewiesen hat. Die Nummerierung beginnt bei 1. Der obige Screenshot wurde nach mehrmaliger Ausführung des Skripts aufgenommen.


Bei der PostgreSQL-Datenbank lauten die Ergebnisse wie folgt:

- Klicken Sie mit der rechten Maustaste auf [1] und anschließend auf [2-3];
- In [4] werden die Daten zu den Steuerklassen übersichtlich angezeigt;
Das Gleiche machen wir für die Konstantentabelle [tbconstantes]:



20.2. Anwendung 2: Steuerberechnung im Batch-Modus

20.2.1. Architektur
Die Steuerberechnungsanwendung in Version 4 verwendete die folgende Architektur:

Die [dao]-Schicht implementiert eine Schnittstelle [InterfaceImpôtsDao]. Wir haben eine Klasse erstellt, die diese Schnittstelle implementiert:
- [TaxDaoWithAdminDataInJsonFile], die Steuerdaten aus einer JSON-Datei abrief. Das war Version 3;
Wir werden die Schnittstelle [InterfaceImpôtsDao] mithilfe einer neuen Klasse [ImpotsDaoWithTaxAdminDataInDatabase] implementieren, die Steuerverwaltungsdaten aus einer Datenbank abruft. Die [dao]-Schicht wird wie zuvor die Ergebnisse in eine JSON-Datei schreiben und Steuerzahlerdaten aus einer Textdatei abrufen. Wir wissen, dass die [business]-Schicht nicht geändert werden muss, wenn wir weiterhin die Schnittstelle [InterfaceImpôtsDao] einhalten.
Die neue Architektur sieht wie folgt aus:

20.2.2. Anwendungskonfiguration

Die Konfigurationsdatei [config_database] bleibt unverändert gegenüber Anwendung 1. Die Konfiguration [config] enthält neue Elemente:
# étape 2 ------
# on complète la configuration de l'application
config.update({
# chemins absolus des fichiers de données
"admindataFilename": f"{script_dir}/../../data/input/admindata.json",
"taxpayersFilename": f"{script_dir}/../../data/input/taxpayersdata.txt",
"errorsFilename": f"{script_dir}/../../data/output/errors.txt",
"resultsFilename": f"{script_dir}/../../data/output/résultats.json"
})
- Zeilen 6–8: die absoluten Pfade der von Anwendung 2 verwendeten Textdateien;
Die Konfiguration der Ebenen [config_layers] entwickelt sich wie folgt:
- Zeilen 3–4: Die [dao]-Schicht wird nun durch die Klasse [TaxDaoWithAdminDataInDatabase] implementiert. Diese Klasse ist neu, implementiert jedoch dieselbe [DaoInterface]-Schnittstelle wie in Version 4 der Anwendungsübung;
- Zeilen 7–8: Die [business]-Schicht wird von der Klasse [ImpôtsMétier] implementiert. Dies ist die Klasse, die in Version 4 der Anwendungsübung verwendet wird;
20.2.3. Die [DAO]-Schicht
Die Implementierungsklasse [ImpotsDaoWithAdminDataInDatabase] für die Schnittstelle [InterfaceImpôtsDao] sieht wie folgt aus:
Anmerkungen
- Zeile 11: Die Klasse [ImpotsDaoWithAdminDataInDatabase] erbt von der in Version 4 vorgestellten Klasse [AbstractImpôtsDao]. Wir wissen, dass letztere die in derselben Version vorgestellte Schnittstelle [InterfaceDao] implementiert. Die Einhaltung dieser Schnittstelle ermöglicht es uns, die [business]-Schicht unverändert zu lassen;
- Zeile 13: Der Klassenkonstruktor erhält das Konfigurationswörterbuch der Anwendung als Parameter;
- Zeile 20: Die übergeordnete Klasse [] wird initialisiert. Sie implementiert teilweise die Schnittstelle [InterfaceDao]:
- [get_taxpayers_data] liest die Datei [taxpayersdata.txt] mit den Steuerzahlerdaten;
- [write_taxpayers_results] schreibt die Ergebnisse in die JSON-Datei [results.json];
- [get_admindata] ist nicht implementiert;
- Zeile 22: Die als Parameter übergebenen Konfigurationen werden gespeichert;
- Zeile 27: Implementierung der Methode [get_admindata] der Schnittstelle [InterfaceDao]:
- Zeilen 28–30: Die Methode [get_admindata] ruft Daten aus der Steuerverwaltung in ein Objekt vom Typ [AdminData] ab und speichert dieses Objekt in [self.__admindata]. Wird die Methode [get_admindata] mehrfach aufgerufen, wird die Datenbank nicht mehrfach abgefragt. Sie wird nur beim ersten Mal abgefragt. Bei nachfolgenden Aufrufen wird das Objekt [self.__admindata] zurückgegeben;
- Zeilen 36–37: Abrufen der [sqlalchemy]-Sitzung, die während der Anwendungskonfiguration durch [config_database] erstellt wurde;
- Zeile 40: Wir rufen die Steuerklassen in einer Liste ab;
- Zeile 43: Wir rufen die Konstanten für die Steuerberechnung ab;
- Zeile 46: Wir erstellen eine Instanz der Klasse [AdminData]. Zur Erinnerung: Diese leitet sich von [BaseEntity] ab;
- Zeilen 48–54: Wir initialisieren die Arrays [limites, coeffr, coeffn] der [AdminData]-Instanz;
- Zeilen 55–56: Initialisieren Sie die anderen Eigenschaften von [AdminData] mit den Konstanten für die Steuerberechnung. Wir haben darauf geachtet, den Eigenschaften der Klassen [AdminData] und [Constantes] dieselben Namen zu geben, was den Code vereinfacht;
- Zeilen 57–58: Die Instanz [AdminData] wird in der [dao]-Schicht gespeichert, um bei nachfolgenden Aufrufen der Methode [get_admindata] zurückgegeben zu werden;
- Zeile 60: Der vom aufrufenden Code angeforderte Wert wird zurückgegeben;
- Zeilen 61–63: Fehlerbehandlung;
- Zeilen 64–67: Die Datenbank wird nur einmal abgefragt. Wir können daher die [sqlalchemy]-Sitzung schließen;
20.2.4. Testen der [dao]-Schicht
In Version 4 dieser Anwendung haben wir eine Testklasse für die [business]-Schicht erstellt. Genauer gesagt testete sie sowohl die [business]- als auch die [DAO]-Schicht. Wir verwenden diesen Test erneut, um zu überprüfen, ob die [DAO]-Schicht wie erwartet funktioniert. Die [business]-Schicht bleibt jedoch unverändert.


Der [TestDaoMétier]-Test sieht wie folgt aus:
import unittest
class TestDaoMétier(unittest.TestCase):
def test_1(self) -> None:
from TaxPayer import TaxPayer
# {'marié': 'oui', 'enfants': 2, 'salaire': 55555,
# 'impôt': 2814, 'surcôte': 0, 'décôte': 0, 'réduction': 0, 'taux': 0.14}
taxpayer = TaxPayer().fromdict({"marié": "oui", "enfants": 2, "salaire": 55555})
métier.calculate_tax(taxpayer, admindata)
# vérification
self.assertAlmostEqual(taxpayer.impôt, 2815, delta=1)
self.assertEqual(taxpayer.décôte, 0)
self.assertEqual(taxpayer.réduction, 0)
self.assertAlmostEqual(taxpayer.taux, 0.14, delta=0.01)
self.assertEqual(taxpayer.surcôte, 0)
…
def test_11(self) -> None:
from TaxPayer import TaxPayer
# {'marié': 'oui', 'enfants': 3, 'salaire': 200000,
# 'impôt': 42842, 'surcôte': 17283, 'décôte': 0, 'réduction': 0, 'taux': 0.41}
taxpayer = TaxPayer().fromdict({'marié': 'oui', 'enfants': 3, 'salaire': 200000})
métier.calculate_tax(taxpayer, admindata)
# vérifications
self.assertAlmostEqual(taxpayer.impôt, 42842, 1)
self.assertEqual(taxpayer.décôte, 0)
self.assertEqual(taxpayer.réduction, 0)
self.assertAlmostEqual(taxpayer.taux, 0.41, delta=0.01)
self.assertAlmostEqual(taxpayer.surcôte, 17283, delta=1)
if __name__ == '__main__':
# on attend un paramètre mysql ou pgres
import sys
syntaxe = f"{sys.argv[0]} mysql / pgres"
erreur = len(sys.argv) != 2
if not erreur:
sgbd = sys.argv[1].lower()
erreur = sgbd != "mysql" and sgbd != "pgres"
if erreur:
print(f"syntaxe : {syntaxe}")
sys.exit()
# on configure l'application
import config
config = config.configure({'sgbd': sgbd})
# couche métier
métier = config['métier']
try:
# admindata
admindata = config['dao'].get_admindata()
except BaseException as ex:
# affichage
print((f"L'erreur suivante s'est produite : {ex}"))
# fin
sys.exit()
# on enève le paramètre reçu par le script
sys.argv.pop()
# on exécute les méthodes de test
print("tests en cours...")
unittest.main()
- Wir werden nicht erneut auf die 11 Tests eingehen, die im Abschnitt |[Business]-Layer-Test Version 4| beschrieben sind;
- Zeilen 37–66: Wir führen das Testskript als normale Anwendung und nicht als UnitTest aus. Zeile 66 löst das UnitTest-Framework aus. In den vorherigen Tests haben wir die Methode [setUp] verwendet, um die Ausführung jedes Tests zu konfigurieren. Da die Funktion [setUp] vor jedem Test ausgeführt wird, haben wir dieselbe Konfiguration elfmal wiederholt. Hier führen wir die Konfiguration nur einmal durch. Sie besteht aus der Definition der globalen Variablen [business] in Zeile 53 und [admindata] in Zeile 56, die dann von den Methoden von [TestDaoBusiness] verwendet werden, zum Beispiel in Zeile 12;
- Zeilen 39–47: Das Testskript erwartet einen Parameter [mysql / pgres], der angibt, ob eine MySQL- oder eine PostgreSQL-Datenbank verwendet wird;
- Zeilen 50–51: Der Test wird konfiguriert;
- Zeile 53: Die [business]-Schicht wird aus der Konfiguration abgerufen;
- Zeile 56: Wir verfahren ebenso mit der [dao]-Schicht. Anschließend rufen wir die [admindata]-Instanz ab, die die zur Berechnung der Steuer erforderlichen Daten enthält;
- Tests haben gezeigt, dass die Methode [unittest.main()] in Zeile 66 den an das Skript übergebenen Parameter [mysql / pgres] nicht ignorierte, sondern ihm stattdessen eine andere Bedeutung zuwies. Zeile 63 stellt sicher, dass diese Methode keine Parameter mehr hat;
Wir erstellen zwei Ausführungskonfigurationen:


Wenn wir eine dieser beiden Konfigurationen ausführen, erhalten wir die folgenden Ergebnisse:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/v05/tests/TestDaoMétier.py mysql
tests en cours...
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s
OK
Process finished with exit code 0
- Zeilen 5 und 7: Alle 11 Tests bestanden;
Beachten Sie, dass diese Tests nur 11 Fälle der Steuerberechnung überprüfen. Ihr Erfolg kann dennoch ausreichen, um uns Vertrauen in die [dao]-Schicht zu geben.
20.2.5. Das Hauptskript


Das Hauptskript [main] ist dasselbe wie in Version 4:
Anmerkungen
- Zeilen 1–10: Wir rufen den Parameter [mysql / pgres] ab, der das zu verwendende DBMS angibt;
- Zeilen 12–14: Die Anwendung wird konfiguriert;
- Zeilen 16–17: Die Klasse [ImpôtsError] wird importiert. Wir benötigen sie in Zeile 38;
- Zeilen 19–21: Wir rufen Referenzen auf die Anwendungsschichten ab;
- Zeile 25: Wir fordern die Daten der Steuerverwaltung aus der [dao]-Schicht an. Die [business]-Schicht benötigt diese zur Berechnung der Steuer;
- Zeile 27: Wir rufen die Daten der Steuerzahler (ID, verheiratet, Kinder, Gehalt) in eine Liste ab;
- Zeilen 29–30: Ist diese Liste leer, wird eine Ausnahme ausgelöst;
- Zeilen 32–35: Berechnung der Steuer für die Elemente in der Liste [taxpayers];
- Zeile 37: Die Ergebnisse werden in die JSON-Datei [results.json] geschrieben;
- Zeilen 38–40: Behandlung etwaiger Fehler;
Um das Skript auszuführen, erstellen wir zwei |Ausführungskonfigurationen|:

Die in der Datei [results.json] erhaltenen Ergebnisse stammen aus Version 4.

20.3. Anwendung 3: Steuerberechnung im interaktiven Modus
Wir stellen nun die Anwendung vor, die eine interaktive Steuerberechnung ermöglicht. Es handelt sich um eine Portierung von Anwendung 2 aus Version 4.


- Das [main]-Skript initiiert den Benutzerdialog mithilfe der Methode [ui.run] der [ui]-Schicht;
- Die [ui]-Schicht:
- nutzt die [dao]-Schicht, um die für die Steuerberechnung benötigten Daten abzurufen;
- fragt den Benutzer nach Informationen zu dem Steuerpflichtigen, für den die Steuer berechnet werden soll;
- nutzt die [business]-Schicht, um diese Berechnung durchzuführen;
Die Datei [config_layers] instanziiert eine zusätzliche Ebene:
Die Klasse [ImpôtsConsole] in den Zeilen 11–12 ist dieselbe wie in |Version 4|.
Das Hauptskript [main] lautet wie folgt:
- Zeilen 1–10: Das Skript erwartet einen Parameter [mysql / pgres], der das zu verwendende DBMS angibt;
- Zeilen 12–14: Die Anwendung wird konfiguriert;
- Zeilen 19–20: Die [ui]-Schicht wird aus der Konfiguration abgerufen;
- Zeile 25: Sie wird ausgeführt;
Die Ergebnisse sind identisch mit denen von |Version 4|. Das kann auch gar nicht anders sein, da alle Schnittstellen aus Version 4 in Version 5 beibehalten wurden.