IX. Exercice d'application - [IMPOTS] avec objets▲
Nous reprenons ici l'exercice décrit précédemment. Nous partons de la version avec fichiers texte. Pour traiter cet exemple avec des objets, nous allons utiliser une architecture à trois couches :
- la couche [DAO] (Data Access Object) s'occupe de l'accès aux données. Dans la suite, ces données seront trouvées d'abord dans un fichier texte, puis dans une base de données MySQL ;
- la couche [metier] s'occupe des problèmes métier, ici le calcul de l'impôt. Elle ne s'occupe pas des données. Celles-ci peuvent avoir deux provenances :
- la couche [DAO] pour les données persistantes,
- la couche [console] pour les données fournies par l'utilisateur ;
- la couche [console] s'occupe des interactions avec l'utilisateur.
Dans la suite, les couches [DAO] et [metier] seront chacune implémentée à l'aide d'une classe. La couche [console] sera elle implémentée par le programme principal.
On supposera également que toutes les implémentations de la couche [DAO] offrent la méthode getData() qui rend un tuple de trois éléments (limites, coeffR, coeffN) qui sont les trois tableaux de données nécessaires au calcul de l'impôt. Dans d'autres langages, on appelle cela une interface. Une interface définit des méthodes (ici getData) que les classes implémentant cette interface doivent avoir.
La couche [metier] sera implémentée par une classe dont le constructeur aura pour paramètre une référence sur la couche [DAO] ce qui assurera la communication entre les deux couches.
IX-A. La couche [DAO]▲
Nous allons réunir les différentes classes nécessaires à l'application dans un même fichier impots.py. Les objets de ce fichier seront ensuite importés dans les scripts les nécessitant.
Nous commençons par le cas où les données sont dans un fichier texte.
Le code de la classe [ImpotsFile] (impots.py) qui implémente la couche [DAO] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
# -*- coding=utf-8 -*-
import
math, sys
# --------------------------------------------------------------------------
# classe d'exceptions propriétaire
class
ImpotsError:
pass
# --------------------------------------------------------------------------
class
Utilitaires:
"""classe de fonctions utilitaires"""
def
cutNewLineChar
(
self,ligne):
# on supprime la marque de fin de ligne si elle existe
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 : le nom du fichier contenant les données des tables limites, coeffR, coeffN
# on ouvre le fichier
data=
open(
IMPOTS,"r"
)
# un objet Utilitaires
u=
Utilitaires
(
)
# création des 3 tables - on suppose que les 3 lignes de IMPOTS sont syntaxiquement correctes
# -- ligne 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])
# -- ligne 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])
# -- ligne 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])
# fin
(
self.limites,self.coeffR,self.coeffN)=(
limites,coeffR,coeffN)
def
getData
(
self):
return
(
self.limites, self.coeffR, self.coeffN)
Notes :
- lignes 7-8 : on définit une classe ImpotsError dérivée de la classe Exception. Cette classe n'ajoute rien à la classe Exception. On l'utilise seulement pour avoir une classe d'exception propriétaire. Ultérieurement cette classe pourrait être enrichie ;
- lignes 11-18 : une classe de méthodes utilitaires. Ici la méthode cutNewLineChar supprime l'éventuelle marque de fin de ligne d'une chaîne de caractères ;
- ligne 25 : l'ouverture du fichier peut lancer l'exception IOError ;
- lignes 32, 39, 46 : on lance l'exception propriétaire ImpotsError ;
- le code est analogue à celui étudié précédemment.
IX-B. La couche [metier]▲
La classe [ImpotsMetier] (impots.py) qui implémente la couche [metier] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
class
ImpotsMetier:
# constructeur
# on récupère un pointeur sur la couche [dao]
def
__init__
(
self, dao):
self.dao=
dao
# calcul de l'impôt
# --------------------------------------------------------------------------
def
calculer
(
self,marie,enfants,salaire):
# marié : oui, non
# enfants : nombre d'enfants
# salaire : salaire annuel
# on demande à la couche [dao] les données nécessaires au calcul
(
limites, coeffR, coeffN)=
self.dao.getData
(
)
# nombre de parts
marie=
marie.lower
(
)
if
(
marie==
"oui"
):
nbParts=
float(
enfants)/
2
+
2
else
:
nbParts=
float(
enfants)/
2
+
1
# une 1/2 part de plus si au moins 3 enfants
if
enfants>=
3
:
nbParts+=
0.5
# revenu imposable
revenuImposable=
0.72
*
salaire
# quotient familial
quotient=
revenuImposable/
nbParts
# est mis à la fin du tableau limites pour arrêter la boucle qui suit
limites[len(
limites)-
1
]=
quotient
# calcul de l'impôt
i=
0
while
quotient>
limites[i] :
i=
i+
1
# du fait qu'on a placé quotient à la fin du tableau limites, la boucle précédente
# ne peut déborder du tableau limites
# maintenant on peut calculer l'impôt
return
math.floor
(
revenuImposable*(
float)(
coeffR[i])-
nbParts*(
float)(
coeffN[i]))
Notes :
- lignes 5-6 : le constructeur de la classe reçoit comme paramètre une référence sur la couche [DAO] ;
- ligne 16 : on utilise la méthode getData de la couche [DAO] pour récupérer les données permettant le calcul de l'impôt ;
IX-C. La couche [console]▲
Le script implémentant la couche [console] (impots-03) est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
# -*- coding=utf-8 -*-
# import du module des classes Impots*
from
impots import
*
# ------------------------------------------------ main
# définition des constantes
DATA=
"data.txt"
RESULTATS=
"resultats.txt"
IMPOTS=
"impots.txt"
# les données nécessaires au calcul de l'impôt ont été placées dans le fichier IMPOTS
# à raison d'une ligne par tableau sous la forme
# val1:val2:val3...
# instanciation couche [metier]
try
:
metier=
ImpotsMetier
(
ImpotsFile
(
IMPOTS))
except
(
IOError
, ImpotsError) as
infos:
print
(
"Une erreur s'est produite : {0}"
.format
(
infos))
sys.exit
(
)
# lecture des données
try
:
data=
open(
DATA,"r"
)
except
:
print
"Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit
(
)
# ouverture fichier des résultats
try
:
resultats=
open(
RESULTATS,"w"
)
except
:
print
"Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit
(
)
# utilitaires
u=
Utilitaires
(
)
# on exploite la ligne courante du fichier des données
ligne=
data.readline
(
)
while
(
ligne !=
''
):
# on enlève l'éventuelle marque de fin de ligne
ligne=
u.cutNewLineChar
(
ligne)
# on récupère les 3 champs marié:enfants:salaire qui forment la ligne
(
marie,enfants,salaire)=
ligne.split
(
","
)
enfants=
int(
enfants)
salaire=
int(
salaire)
# on calcule l'impôt
impot=
metier.calculer
(
marie,enfants,salaire)
# on inscrit le résultat
resultats.write
(
"{0}:{1}:{2}:{3}
\n
"
.format
(
marie,enfants,salaire,impot))
# on lit une nouvelle ligne
ligne=
data.readline
(
)
# on ferme les fichiers
data.close
(
)
resultats.close
(
)
Notes :
- ligne 4 : on importe tous les objets du fichier impots.py qui contient les définitions des classes. Ceci fait, on peut utiliser ces objets comme s'ils étaient dans le même fichier que le script ;
- lignes 17-21 : on instancie à la fois la couche [DAO] et la couche [metier] avec une gestion des exceptions éventuelles ;
- ligne 18 : on instancie la couche [DAO] puis la couche [metier]. On mémorise la référence sur la couche [metier] ;
- ligne 19 : on gère les deux exceptions qui peuvent se produire ;
IX-D. Résultats▲
Ceux déjà obtenus dans les versions avec tableaux et fichiers.
Le fichier des données impots.txt :
2.
3.
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
Le fichier des données data.txt :
2.
3.
4.
5.
6.
oui,2,200000
non,2,200000
oui,3,200000
non,3,200000
oui,5,50000
non,0,3000000
Les fichier resultats.txt des résultats obtenus :
2.
3.
4.
5.
6.
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