8. Esercizio pratico - [IMPOTS] con l'architettur e a livelli
![]() |
Riprendiamo qui l’esercizio descritto al paragrafo 4.1. Partiamo dalla versione con file di testo descritta al paragrafo 4.3. Per elaborare questo esempio con gli oggetti, utilizzeremo un’architettura a tre livelli:
![]() |
- il livello [dao] (Data Access Object) si occupa dell'accesso ai dati. Di seguito, questi dati saranno reperiti dapprima in un file di testo, poi in un database MySQL;
- il livello [metier] si occupa delle logiche di business, in questo caso il calcolo delle imposte. Non si occupa dei dati. Questi ultimi possono provenire da due fonti:
- il livello [dao] per i dati persistenti;
- il livello [console] per i dati forniti dall’utente.
- il livello [console] si occupa delle interazioni con l'utente.
Di seguito, i livelli [dao] e [metier] saranno implementati ciascuno tramite una classe. Il livello [console] sarà invece implementato dal programma principale.
Si suppone che tutte le implementazioni del livello [dao] forniscano il metodo getData(), che restituisce una tupla di tre elementi (limiti, coeffR, coeffN), ovvero i tre array di dati necessari al calcolo dell’imposta. In altri linguaggi, questa struttura viene definita interfaccia. Un’interfaccia definisce dei metodi (in questo caso getData) che le classi che la implementano devono possedere.
Il livello [metier] sarà implementato da una classe il cui costruttore avrà come parametro un riferimento al livello [dao], il che garantirà la comunicazione tra i due livelli.
8.1. Il livello [dao]
Riuniremo le diverse classi necessarie all’applicazione in un unico file impots.py. Gli oggetti di questo file verranno poi importati negli script che ne hanno bisogno.
Iniziamo con il caso in cui i dati si trovino in un file di testo, come nell’esempio del paragrafo 4.3.
![]() |
Il codice della classe [ImpotsFile] (impots.py), che implementa il livello [dao], è il seguente:
# -*- coding=utf-8 -*-
import math, sys
# --------------------------------------------------------------------------
# classe di eccezione proprietaria
class ImpotsError:
pass
# --------------------------------------------------------------------------
class Utilitaires:
"""classe de fonctions utilitaires"""
def cutNewLineChar(self,ligne):
# si rimuove il carattere di fine riga se presente
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: il nome del file contenente i dati delle tabelle limite, coeffR, coeffN
# si apre il file
data=open(IMPOTS,"r")
# un oggetto Utilità
u=Utilitaires()
# creazione delle 3 tabelle - si suppone che le 3 righe di IMPOTS siano sintatticamente corrette
# -- riga 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])
# -- riga 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])
# -- riga 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])
# fine
(self.limites,self.coeffR,self.coeffN)=(limites,coeffR,coeffN)
def getData(self):
return (self.limites, self.coeffR, self.coeffN)
Note:
- righe 7-8: si definisce una classe ImpotsError derivata dalla classe Exception. Questa classe non aggiunge nulla alla classe Exception. Viene utilizzata solo per disporre di una classe di eccezioni proprietaria. In futuro questa classe potrebbe essere arricchita;
- righe 11-18: una classe di metodi di utilità. Qui il metodo cutNewLineChar rimuove l'eventuale carattere di fine riga da una stringa di caratteri;
- riga 25: l’apertura del file può generare l’eccezione IOError;
- righe 32, 39, 46: viene generata l’eccezione proprietaria ImpotsError;
- il codice è analogo a quello esaminato nell'esempio del paragrafo 4.3.
8.2. Il livello [metier]
![]() |
La classe [ImpotsMetier] (impots.py) che implementa il livello [metier] è la seguente:
class ImpotsMetier:
# costruttore
# si recupera un puntatore al livello [dao]
def __init__(self, dao):
self.dao=dao
# calcolo dell'imposta
# --------------------------------------------------------------------------
def calculer(self,marie,enfants,salaire):
# stato civile: sì, no
# figli: numero di figli
# stipendio: stipendio annuo
# i dati necessari per il calcolo vengono richiesti al livello [dao]
(limites, coeffR, coeffN)=self.dao.getData()
# numero di quote
marie=marie.lower()
if(marie=="oui"):
nbParts=float(enfants)/2+2
else:
nbParts=float(enfants)/2+1
# una quota aggiuntiva di 1/2 se ci sono almeno 3 figli
if enfants>=3:
nbParts+=0.5
# reddito imponibile
revenuImposable=0.72*salaire
# quoziente familiare
quotient=revenuImposable/nbParts
# viene inserito alla fine della tabella dei limiti per interrompere il ciclo successivo
limites[len(limites)-1]=quotient
# calcolo dell'imposta
i=0
while quotient>limites[i] :
i=i+1
# poiché il quoziente è stato inserito alla fine della tabella dei limiti, il ciclo precedente
# non può andare oltre i limiti della tabella
# ora è possibile calcolare l'imposta
return math.floor(revenuImposable*(float)(coeffR[i])-nbParts*(float)(coeffN[i]))
Note:
- righe 5-6: il costruttore della classe riceve come parametro un riferimento al livello [dao];
- riga 16: si utilizza il metodo getData del livello [dao] per recuperare i dati necessari al calcolo dell’imposta;
- il resto del codice è analogo a quello esaminato nell’esempio del paragrafo 4.3.
8.3. Il livello [console]
![]() |
Lo script che implementa il livello [console] (impots-03) è il seguente:
# -*- coding=utf-8 -*-
# importazione del modulo delle classi Impostazioni*
from impots import *
# ------------------------------------------------ main
# definizione delle costanti
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"
# i dati necessari per il calcolo dell'imposta sono stati inseriti nel file IMPOTS
# con una riga per ogni tabella nella forma
# val1:val2:val3,...
# istanza del livello [metier]
try:
metier=ImpotsMetier(ImpotsFile(IMPOTS))
except (IOError, ImpotsError) as infos:
print ("Une erreur s'est produite : {0}".format(infos))
sys.exit()
# lettura dei dati
try:
data=open(DATA,"r")
except:
print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit()
# apertura del file dei risultati
try:
resultats=open(RESULTATS,"w")
except:
print "Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit()
# utilità
u=Utilitaires()
# elaborazione della riga corrente del file dei dati
ligne=data.readline()
while(ligne != ''):
# si rimuove l'eventuale carattere di fine riga
ligne=u.cutNewLineChar(ligne)
# si recuperano i 3 campi coniuge:figli:stipendio che compongono la riga
(marie,enfants,salaire)=ligne.split(",")
enfants=int(enfants)
salaire=int(salaire)
# si calcola l’imposta
impot=metier.calculer(marie,enfants,salaire)
# si inserisce il risultato
resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
# si legge una nuova riga
ligne=data.readline()
# si chiudono i file
data.close()
resultats.close()
Note:
- riga 4: si importano tutti gli oggetti dal file impots.py che contiene le definizioni delle classi. Una volta fatto ciò, è possibile utilizzare questi oggetti come se si trovassero nello stesso file dello script;
- righe 17-21: si istanziano contemporaneamente il livello [dao] e il livello [metier] con la gestione delle eventuali eccezioni;
- riga 18: si istanzia il livello [dao] e poi il livello [metier]. Si memorizza il riferimento al livello [metier];
- riga 19: si gestiscono le due eccezioni che possono verificarsi;
- il resto del codice è analogo a quello esaminato nell’esempio del paragrafo 4.3.
8.4. Résultats
Quelli già ottenuti nelle versioni con tabelle e file.
Il file dei dati 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
Il file di dati data.txt:
I file resultats.txt dei risultati ottenuti:




