Skip to content

8. Anwendungsübung – [IMPOTS] mit der Schichtenarchitektur „

Wir greifen hier die in Abschnitt 4.1 beschriebene Übung wieder auf. Wir gehen von der in Abschnitt 4.3 beschriebenen Version mit Textdateien aus. Um dieses Beispiel mit Objekten zu bearbeiten, verwenden wir eine dreischichtige Architektur:

  • Die Schicht [dao] (Data Access Object) ist für den Datenzugriff zuständig. Im weiteren Verlauf werden diese Daten zunächst in einer Textdatei und anschließend in einer Datenbank MySQL gefunden;
  • die Schicht [metier] befasst sich mit den geschäftlichen Aufgaben, in diesem Fall der Steuerberechnung. Sie befasst sich nicht mit den Daten. Diese können aus zwei Quellen stammen:
    • die Schicht [dao] für persistente Daten;
    • die Schicht [console] für vom Benutzer bereitgestellte Daten.
  • Die Schicht [console] ist für die Interaktionen mit dem Benutzer zuständig.

Im Folgenden werden die Schichten [dao] und [metier] jeweils mithilfe einer Klasse implementiert. Die Schicht [console] wird hingegen durch das Hauptprogramm implementiert.

Es wird angenommen, dass alle Implementierungen der Schicht [dao] die Methode getData() bereitstellen, die ein Tupel mit drei Elementen (Grenzwerte, coeffR, coeffN) zurückgibt, bei denen es sich um die drei für die Steuerberechnung erforderlichen Datenarrays handelt. In anderen Sprachen wird dies als Schnittstelle bezeichnet. Eine Schnittstelle definiert Methoden (hier getData), über die die Klassen verfügen müssen, die diese Schnittstelle implementieren.

Die Schicht [metier] wird durch eine Klasse implementiert, deren Konstruktor als Parameter eine Referenz auf die Schicht [dao] erhält, wodurch die Kommunikation zwischen den beiden Schichten gewährleistet wird.

8.1. Die Schicht [dao]

Wir werden die verschiedenen für die Anwendung erforderlichen Klassen in einer einzigen Datei impots.py zusammenfassen. Die Objekte dieser Datei werden anschließend in die Skripte importiert, die sie benötigen.

Wir beginnen mit dem Fall, in dem sich die Daten in einer Textdatei befinden, wie im Beispiel in Abschnitt 4.3.

Der Code der Klasse [ImpotsFile] „ “ (impots.py), die die Schicht [dao] implementiert, lautet wie folgt:


# -*- coding=utf-8 -*-

import math, sys

# --------------------------------------------------------------------------
# eigene Ausnahmeklasse
class ImpotsError:
    pass

# --------------------------------------------------------------------------
class Utilitaires:
    """classe de fonctions utilitaires"""
    def cutNewLineChar(self,ligne):
        # Das Zeilenendezeichen wird entfernt, falls vorhanden
        l=len(ligne)
        while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
            l-=1
        return(ligne[0:l])

# --------------------------------------------------------------------------
class ImpotsFile:
    def __init__(self,IMPOTS):
        # IMPOTS: Name der Datei, die die Daten der Grenzwerttabellen enthält, coeffR, coeffN
        # Die Datei wird geöffnet
        data=open(IMPOTS,"r")
        # ein Objekt „Utilitaires“
        u=Utilitaires()
        # Erstellung der 3 Tabellen – es wird davon ausgegangen, dass die 3 Zeilen von IMPOTS syntaktisch korrekt sind
        # -- Zeile 1
        ligne=data.readline()
        if ligne== '':
            raise ImpotsError("La premiere ligne du fichier {0} est absente".format(IMPOTS))
        limites=u.cutNewLineChar(ligne).split(":")
        for i in range(len(limites)):
            limites[i]=int(limites[i])
        # – Zeile 2
        ligne=data.readline()
        if ligne== '':
            raise ImpotsError("La deuxieme ligne du fichier {0} est absente".format(IMPOTS))
        coeffR=u.cutNewLineChar(ligne).split(":")
        for i in range(len(coeffR)):
            coeffR[i]=float(coeffR[i])
        # – Zeile 3
        ligne=data.readline()
        if ligne== '':
            raise ImpotsError("La troisieme ligne du fichier {0} est absente".format(IMPOTS))
        coeffN=u.cutNewLineChar(ligne).split(":")
        for i in range(len(coeffN)):
            coeffN[i]=float(coeffN[i])
        # Ende
        (self.limites,self.coeffR,self.coeffN)=(limites,coeffR,coeffN)

    def getData(self):
        return (self.limites, self.coeffR, self.coeffN)

Anmerkungen:

  • Zeilen 7–8: Es wird eine Klasse „ImpotsError“ definiert, die von der Klasse „Exception“ abgeleitet ist. Diese Klasse enthält keine zusätzlichen Elemente gegenüber der Klasse „Exception“. Sie wird lediglich verwendet, um eine proprietäre Ausnahmeklasse zu erhalten. Später könnte diese Klasse erweitert werden;
  • Zeilen 11–18: eine Klasse mit Hilfsmethoden. Hier entfernt die Methode cutNewLineChar eventuelle Zeilenendezeichen aus einer Zeichenkette;
  • Zeile 25: Beim Öffnen der Datei kann die Ausnahme IOError ausgelöst werden;
  • Zeilen 32, 39, 46: Die benutzerdefinierte Ausnahme ImpotsError wird ausgelöst;
  • Der Code entspricht dem im Beispiel in Abschnitt 4.3 behandelten.

8.2. Die Schicht [metier]

Die Klasse [ImpotsMetier] „ “ (impots.py), die die Schicht [metier] implementiert, lautet wie folgt:


class ImpotsMetier:

    # Konstruktor
    # es wird ein Zeiger auf die Schicht [dao] abgerufen
    def __init__(self, dao):
        self.dao=dao

    # Steuerberechnung
    # --------------------------------------------------------------------------
    def calculer(self,marie,enfants,salaire):
        # verheiratet: ja, nein
        # Kinder: Anzahl der Kinder
        # Gehalt: Jahresgehalt

        # Die für die Berechnung erforderlichen Daten werden aus der Schicht [dao] abgerufen
        (limites, coeffR, coeffN)=self.dao.getData()

       # Anzahl der Anteile
        marie=marie.lower()
        if(marie=="oui"):
            nbParts=float(enfants)/2+2
        else:
            nbParts=float(enfants)/2+1
        # ein halber Anteil zusätzlich, wenn mindestens 3 Kinder vorhanden sind
        if enfants>=3:
            nbParts+=0.5
        # steuerpflichtiges Einkommen
        revenuImposable=0.72*salaire
        # Familienquotient
        quotient=revenuImposable/nbParts
        # wird am Ende der Tabelle „Grenzwerte“ eingefügt, um die folgende Schleife zu beenden
        limites[len(limites)-1]=quotient
        # Steuerberechnung
        i=0
        while quotient>limites[i] :
            i=i+1
        # Da der Familienquotient am Ende des Grenzwert-Arrays platziert wurde, wird die vorherige Schleife
        # kann nicht über die Grenzen des Grenzwertetabells hinausgehen
        # Nun kann die Steuer berechnet werden
        return math.floor(revenuImposable*(float)(coeffR[i])-nbParts*(float)(coeffN[i]))

Anmerkungen:

  • Zeilen 5–6: Der Konstruktor der Klasse erhält als Parameter eine Referenz auf die Schicht [dao];
  • Zeile 16: Es wird die Methode getData der Schicht [dao] verwendet, um die Daten für die Steuerberechnung abzurufen;
  • der restliche Code entspricht dem im Beispiel in Abschnitt 4.3 behandelten.

8.3. Die Schicht [console]

Das Skript zur Implementierung der Schicht [console] (impots-03) lautet wie folgt:


# -*- coding=utf-8 -*-

# Import des Moduls „Impots*“
from impots import *

# ------------------------------------------------ main
# Definition der Konstanten
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

# Die für die Steuerberechnung erforderlichen Daten wurden in die Datei IMPOTS gespeichert
# mit jeweils einer Zeile pro Tabelle in der Form
# val1:val2:val3,...

# Instanziierung der Ebene [metier]
try:
    metier=ImpotsMetier(ImpotsFile(IMPOTS))
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    sys.exit()

# Daten auslesen
try:
    data=open(DATA,"r")
except:
    print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
    sys.exit()

# Ergebnisdatei öffnen
try:
    resultats=open(RESULTATS,"w")
except:  
    print "Impossible de creer le fichier des résultats [RESULTATS]"
    sys.exit()

# Hilfsprogramme
u=Utilitaires()

# Auswertung der aktuellen Zeile der Datendatei
ligne=data.readline()
while(ligne != ''):
    # Eventuelles Zeilenendezeichen wird entfernt
    ligne=u.cutNewLineChar(ligne)
    # Die drei Felder „verheiratet:Kinder:Gehalt“, aus denen die Zeile besteht, werden abgerufen
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # Die Steuer wird berechnet
    impot=metier.calculer(marie,enfants,salaire)
    # Das Ergebnis wird eingetragen
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # eine neue Zeile wird gelesen
    ligne=data.readline()
# Die Dateien werden geschlossen
data.close()
resultats.close()

Anmerkungen:

  • Zeile 4: Es werden alle Objekte aus der Datei impots.py importiert, die die Klassendefinitionen enthält. Anschließend können diese Objekte so verwendet werden, als befänden sie sich in derselben Datei wie das Skript;
  • Zeilen 17–21: Es werden sowohl die Schicht [dao] als auch die Schicht [metier] instanziiert, wobei mögliche Ausnahmen behandelt werden;
  • Zeile 18: Wir instanziieren die Schicht [dao] und anschließend die Schicht [metier]. Wir speichern die Referenz auf die Schicht [metier];
  • Zeile 19: Die beiden Ausnahmen, die auftreten können, werden behandelt;
  • Der restliche Code entspricht dem im Beispiel in Abschnitt 4.3 behandelten.

8.4. Résultats

Die bereits in den Versionen mit Arrays und Dateien erhaltenen Ergebnisse.

Die Datendatei impots.txt:

12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:2072.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062

Die Datendatei data.txt:

oui,2,200000
non,2,200000
oui,3,200000
non,3,200000
oui,5,50000
non,0,3000000

Die Datei resultats.txt mit den erzielten Ergebnissen:

oui:2:200000:22504.0
non:2:200000:33388.0
oui:3:200000:16400.0
non:3:200000:22504.0
oui:5:50000:0.0
non:0:3000000:1354938.0