8. تمرين تطبيقي - [IMPOTS] مع بنية ذات الطبقات
![]() |
نستأنف هنا التمرين الموصوف في الفقرة 4.1. نبدأ من الإصدار الذي يستخدم ملفات نصية الموصوف في الفقرة 4.3. لمعالجة هذا المثال باستخدام الكائنات، سنستخدم بنية ثلاثية الطبقات:
![]() |
- تتولى الطبقة [dao] (كائن الوصول إلى البيانات) مهمة الوصول إلى البيانات. وفيما يلي، سيتم العثور على هذه البيانات أولاً في ملف نصي، ثم في قاعدة بيانات MySQL؛
- الطبقة [metier] تتولى معالجة المشكلات المتعلقة بالأنشطة التجارية، وهي في هذه الحالة حساب الضريبة. ولا تتولى هذه الطبقة معالجة البيانات. ويمكن أن تكون لهذه البيانات مصدران:
- الطبقة [dao] للبيانات الدائمة؛
- الطبقة [console] للبيانات التي يقدمها المستخدم.
- تتولى الطبقة [console] إدارة التفاعلات مع المستخدم.
فيما يلي، سيتم تنفيذ كل من الطبقتين [dao] و [metier] باستخدام فئة. أما الطبقة [console] فسيتم تنفيذها بواسطة البرنامج الرئيسي.
سنفترض أن جميع عمليات تنفيذ الطبقة [dao] توفر الطريقة getData() التي تُرجع مجموعة مكونة من ثلاثة عناصر (الحدود، 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")
# كائن «أدوات مساعدة»
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])
# -- السطر 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])
# -- السطر 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])
# النهاية
(self.limites,self.coeffR,self.coeffN)=(limites,coeffR,coeffN)
def getData(self):
return (self.limites, self.coeffR, self.coeffN)
ملاحظات:
- السطران 7-8: يتم تعريف فئة ImpotsError مشتقة من الفئة Exception. لا تضيف هذه الفئة أي شيء إلى الفئة Exception. يتم استخدامها فقط للحصول على فئة استثناء خاصة. وقد يتم توسيع هذه الفئة لاحقًا؛
- الأسطر 11-18: فئة من الأساليب المساعدة. هنا، تقوم الأسلوب cutNewLineChar بإزالة علامة نهاية السطر (إن وجدت) من سلسلة الأحرف؛
- السطر 25: قد يؤدي فتح الملف إلى إثارة الاستثناء IOError؛
- الأسطر 32 و39 و46: يتم إثارة الاستثناء الخاص 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
# نصف حصة إضافية إذا كان عدد الأطفال 3 على الأقل
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]))
ملاحظات:
- السطران 5-6: يتلقى منشئ الفئة كمعلمة مرجعًا إلى الطبقة [dao]؛
- السطر 16: يتم استخدام الطريقة getData الخاصة بالطبقة [dao] لاسترداد البيانات التي تسمح بحساب الضريبة؛
- أما باقي الكود فهو مشابه للكود الذي تمت دراسته في المثال الوارد في الفقرة 4.3.
8.3. الطبقة [console]
![]() |
النص البرمجي الذي ينفذ الطبقة [console] (impots-03) هو التالي:
# -*- coding=utf-8 -*-
# استيراد الوحدة النمطية الخاصة بفئات الضرائب*
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)
# استرجاع الحقول الثلاثة «متزوج:أطفال:راتب» التي تشكل السطر
(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()
ملاحظات:
- السطر 4: يتم استيراد جميع الكائنات من الملف impots.py الذي يحتوي على تعريفات الفئات. وبذلك، يمكن استخدام هذه الكائنات كما لو كانت موجودة في نفس ملف البرنامج النصي؛
- الأسطر 17-21: يتم إنشاء مثيل لكل من الطبقة [dao] والطبقة [metier] مع معالجة الاستثناءات المحتملة؛
- السطر 18: يتم إنشاء مثيل للطبقة [dao] ثم للطبقة [metier]. يتم تخزين المرجع في الطبقة [metier]؛
- السطر 19: يتم التعامل مع الاستثناءين اللذين قد يحدثان؛
- أما باقي الكود فهو مشابه للكود الذي تمت دراسته في المثال الوارد في الفقرة 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:
ملفات resultats.txt للنتائج التي تم الحصول عليها:




