8. 应用练习 – [税费计算] 采用分层架构
![]() |
在此,我们将重新审视第4.1节中描述的练习。我们将从第4.3节中描述的文本文件版本开始。为了使用对象处理这个示例,我们将采用三层架构:
![]() |
- [DAO](数据访问对象)层负责数据访问。在接下来的内容中,这些数据将首先从文本文件中获取,随后从MySQL数据库中获取;
- [业务]层负责处理业务逻辑,在本例中即税费计算。它不处理数据。这些数据可来自两个来源:
- 用于持久化数据的 [DAO] 层;
- [控制台]层,用于处理用户提供的数据。
- [控制台]层负责处理与用户的交互。
在接下来的内容中,[DAO]层和[业务]层将分别通过一个类来实现。[控制台]层将由主程序来实现。
我们将假设 [dao] 层的所有实现都提供了 getData() 方法,该方法返回一个包含三个元素(limits、coeffR、coeffN)的元组——这是计算税额所需的三个数据数组。在其他语言中,这被称为接口。接口定义了方法(此处为 getData),实现该接口的类必须具备这些方法。
[business] 层将由一个类实现,该类的构造函数将 [dao] 层的引用作为参数,以确保两层之间的通信。
8.1. [DAO] 层
我们将把应用程序所需的各个类合并到一个文件中,即 impots.py。随后,该文件中的对象将被导入到需要它们的脚本中。
我们首先从数据位于文本文件的情况开始,如第 4.3 节中的示例所示。
![]() |
实现 [DAO] 层的 [ ImpotsFile] 类(impots.py)的代码如下:
注释:
- 第 7-8 行:我们定义了一个从 Exception 类派生的 ImpotsError 类。该类并未对 Exception 类进行任何扩展,我们仅将其用作自定义异常类。该类日后可能进行扩展;
- 第 11–18 行:一个包含实用方法的类。其中,cutNewLineChar 方法用于从字符串中移除所有换行符;
- 第 25 行:打开文件可能会抛出 IOError 异常;
- 第 32、39、46 行:抛出自定义的 ImpotsError 异常;
- 该代码与第 4.3 节示例中的代码类似。
8.2. [业务]层
![]() |
实现[业务]层的[ ImpotsMetier]类(impots.py)如下:
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]))
注:
- 第 5-6 行:类构造函数将 [dao] 层的引用作为参数接收;
- 第16行:我们使用[dao]层的getData方法来获取计算税款所需的数据;
- 其余代码与第 4.3 节示例中的内容类似。
8.3. [console] 层
![]() |
实现 [console] 层的脚本(impots-03)如下:
注:
- 第 4 行:我们从 impots.py 文件中导入所有对象,该文件包含类定义。完成此操作后,我们可以像这些对象与脚本位于同一文件中一样使用它们;
- 第 17–21 行:我们分别实例化 [dao] 层和 [business] 层,并处理可能出现的异常;
- 第 18 行:我们先实例化 [dao] 层,然后实例化 [business] 层。我们将 [business] 层的引用存储起来;
- 第 19 行:我们处理可能发生的两种异常;
- 其余代码与第 4.3 节示例中的内容类似。
8.4. 结果
与使用数组和文件的版本中已获得的结果相同。
数据文件 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
数据文件 data.txt:
包含结果的 results.txt 文件:




