Skip to content

8. تمرین عملی – [IMPOTS] با معماری لایه‌ای

در اینجا تمرین توصیف‌شده در بخش 4.1 را مجدداً بررسی می‌کنیم. ما با نسخهٔ فایل متنی توصیف‌شده در بخش 4.3 شروع می‌کنیم. برای پردازش این مثال با استفاده از اشیاء، از معماری سه‌سطحی استفاده خواهیم کرد:

  • لایه [dao] (ابژه دسترسی به داده) دسترسی به داده را مدیریت می‌کند. در ادامه، این داده‌ها ابتدا در یک فایل متنی و سپس در یک پایگاه داده MySQL یافت خواهند شد؛
  • لایه [metier] منطق کسب‌وکار را مدیریت می‌کند، در این مورد محاسبه مالیات. این لایه خودِ داده‌ها را پردازش نمی‌کند. این داده‌ها ممکن است از دو منبع تأمین شوند:
    • لایه [dao] برای داده‌های پایدار؛
    • لایه [console] برای داده‌های ارائه‌شده توسط کاربر.
  • لایه [console] تعاملات کاربر را مدیریت می‌کند.

در ادامه، لایه‌های [dao] و [metier] هر کدام با استفاده از یک کلاس پیاده‌سازی خواهند شد. لایه [console] توسط برنامه اصلی پیاده‌سازی خواهد شد.

ما فرض می‌کنیم که تمام پیاده‌سازی‌های لایه [dao] متد getData() را فراهم می‌کنند که یک تپلی از سه عنصر (limits, coeffR, coeffN)، که سه آرایهٔ داده‌ای مورد نیاز برای محاسبهٔ مالیات هستند. در زبان‌های دیگر، این به عنوان یک رابط شناخته می‌شود. یک رابط متدها را تعریف می‌کند (در این مورد، getData) که کلاس‌های پیاده‌ساز این رابط باید آن‌ها را فراهم کنند.

لایه [metier] توسط کلاسی پیاده‌سازی خواهد شد که سازنده‌اش یک ارجاع به لایه [dao] را به‌عنوان پارامتر می‌پذیرد و بدین ترتیب ارتباط بین دو لایه را تضمین می‌کند.

8.1. لایه [dao]

ما کلاس‌های مختلف مورد نیاز برای برنامه را در یک فایل واحد به نام impots.py ترکیب خواهیم کرد. سپس اشیاء این فایل وارد اسکریپت‌هایی می‌شوند که به آن‌ها نیاز دارند.

ما با سناریویی شروع می‌کنیم که در آن داده‌ها در یک فایل متنی قرار دارند، همانند مثال در بند 4.3.

کد کلاس [ImpotsFile] (impots.py)، که لایه [dao] را پیاده‌سازی می‌کند، به شرح زیر است:


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

import math, sys

# --------------------------------------------------------------------------
# کلاس استثنای مالک
class ImpotsError:
    pass

# --------------------------------------------------------------------------
class Utilitaires:
    """classe de fonctions utilitaires"""
    def cutNewLineChar(self,ligne):
        # کاراکتر پایان خط در صورت وجود حذف می‌شود
        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: نام فایلی که حاوی داده‌های جدول مرزی است، coeffR، coeffN
        # فایل باز می‌شود
        data=open(IMPOTS,"r")
        #یک شیء Utilities
        u=Utilitaires()
        # ایجاد ۳ جدول – فرض می‌کنیم که ۳ خط در IMPOTS از نظر نحوی صحیح هستند
        #-- خط 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=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=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])
        #پایان
        (self.limites,self.coeffR,self.coeffN)=(limites,coeffR,coeffN)

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

یادداشت‌ها:

  • خطوط ۷–۸: کلاسی به نام ImpotsError تعریف شده که از کلاس Exception مشتق شده است. این کلاس هیچ چیزی به کلاس Exception اضافه نمی‌کند. این کلاس صرفاً برای ارائه یک کلاس استثنای سفارشی استفاده می‌شود. ممکن است این کلاس در آینده توسعه یابد؛
  • خطوط ۱۱–۱۸: کلاسی از متدهای ابزار. در اینجا، متد cutNewLineChar هرگونه کاراکتر پایان خط را از یک رشته حذف می‌کند؛
  • خط ۲۵: باز کردن فایل ممکن است باعث ایجاد استثنای IOError شود؛
  • خطوط ۳۲، ۳۹، ۴۶: استثنای سفارشی ImpotsError پرتاب می‌شود؛
  • این کد مشابه کدی است که در مثال بخش 4.3 بررسی شده است.

8.2. لایه [metier]

کلاس [ImpotsMetier] (impots.py)، که لایه [metier] را پیاده‌سازی می‌کند، به شرح زیر است:


class ImpotsMetier:

    # سازنده
    #یک اشاره به لایه [dao] بازیابی می‌شود
    def __init__(self, dao):
        self.dao=dao

    #محاسبه مالیات
    # --------------------------------------------------------------------------
    def calculer(self,marie,enfants,salaire):
        # متأهل: بله، خیر
        # فرزندان: تعداد فرزندان
        # حقوق: حقوق سالانه

        #داده‌های مورد نیاز برای محاسبه از لایه [dao] استخراج می‌شوند
        (limites, coeffR, coeffN)=self.dao.getData()

       # تعداد سهام
        marie=marie.lower()
        if(marie=="oui"):
            nbParts=float(enfants)/2+2
        else:
            nbParts=float(enfants)/2+1
        # نیم سهم اضافی در صورت وجود حداقل ۳ فرزند
        if enfants>=3:
            nbParts+=0.5
        # درآمد مشمول مالیات
        revenuImposable=0.72*salaire
        # ضریب خانوادگی
        quotient=revenuImposable/nbParts
        #در انتهای جدول محدودیت‌ها قرار می‌گیرد تا حلقهٔ زیر را خاتمه دهد
        limites[len(limites)-1]=quotient
        #محاسبه مالیات
        i=0
        while quotient>limites[i] :
            i=i+1
        # از آنجا که ضریب خانواده در انتهای جدول محدودیت‌ها قرار داده شده است، حلقه قبلی
        # نمی‌تواند از آرایه محدودیت‌ها فراتر رود
        # اکنون می‌توانیم مالیات را محاسبه کنیم
        return math.floor(revenuImposable*(float)(coeffR[i])-nbParts*(float)(coeffN[i]))

یادداشت‌ها:

  • خطوط ۵–۶: سازنده کلاس یک مرجع به لایه [dao] را به‌عنوان پارامتر می‌پذیرد؛
  • خط ۱۶: متد getData از لایه [dao] برای بازیابی داده‌های مورد نیاز برای محاسبه مالیات استفاده می‌شود؛
  • بقیه کد مشابه مثالی است که در بخش 4.3 بررسی شده است.

8.3. لایه [console]

اسکریپت پیاده‌سازی لایه [console] (impots-03) به شرح زیر است:


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

#وارد کردن ماژول کلاس 'Impots*'
from impots import *

# ------------------------------------------------ main
# تعریف ثابت‌ها
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"

#داده‌های مورد نیاز برای محاسبه مالیات در فایل IMPOTS قرار داده شده‌اند
# با یک خط برای هر جدول به شکل
# val1:val2:val3,...

# نمونهسازی لایه [metier]
try:
    metier=ImpotsMetier(ImpotsFile(IMPOTS))
except (IOError, ImpotsError) as infos:
    print ("Une erreur s'est produite : {0}".format(infos))
    sys.exit()

# خواندن داده‌ها
try:
    data=open(DATA,"r")
except:
    print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
    sys.exit()

# باز کردن فایل نتایج
try:
    resultats=open(RESULTATS,"w")
except:  
    print "Impossible de creer le fichier des résultats [RESULTATS]"
    sys.exit()

# مفیدیت‌ها
u=Utilitaires()

# پردازش خط فعلی فایل داده
ligne=data.readline()
while(ligne != ''):
    # حذف هر کاراکتر پایان خط
    ligne=u.cutNewLineChar(ligne)
    # استخراج سه فیلد «married:children:salary» که خط را تشکیل می‌دهند
    (marie,enfants,salaire)=ligne.split(",")
    enfants=int(enfants)
    salaire=int(salaire)
    # مالیات محاسبه می‌شود
    impot=metier.calculer(marie,enfants,salaire)
    # نتیجه را می‌نویسم
    resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
    # خط جدید را می‌خوانیم
    ligne=data.readline()
#بستن فایل‌ها
data.close()
resultats.close()

یادداشت‌ها:

  • خط ۴: تمام اشیاء از فایل impots.py وارد می‌شوند که شامل تعریف کلاس‌ها است. پس از انجام این کار، می‌توان از این اشیاء استفاده کرد، گویی که در همان فایلی هستند که اسکریپت در آن قرار دارد؛
  • خطوط ۱۷–۲۱: هر دو لایه [dao] و [metier] با مدیریت هرگونه استثنا ایجاد می‌شوند؛
  • خط ۱۸: ما لایه [dao] و سپس لایه [metier] را نمونه سازی می‌کنیم. ما مرجع لایه [metier] را ذخیره می‌کنیم؛
  • خط ۱۹: دو استثنایی که ممکن است رخ دهند، مدیریت می‌شوند؛
  • بقیه کد مشابه کدی است که در مثال بخش 4.3 بررسی شده است.

8.4. Résultats

آنها قبلاً در نسخه‌هایی که از آرایه‌ها و فایل‌ها استفاده می‌کنند، به دست آمده‌اند.

فایل داده 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:

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

فایل‌های resultats.txt حاوی نتایج به‌دست‌آمده:

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