الأصناف والدوال المجردة
جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 25 من 64.
الفئات المجردة (Abstract classes) هي فئات لا يمكن إنشاء كائنات منها بشكل مباشر، وتحتوي على دوال مجردة (abstract methods) يجب تنفيذها بواسطة الفئات الفرعية (subclasses).
فكر فيها كأنها نموذج: الفئة المجردة تشبه كود نموذج إلزامي حيث تكون بعض الحقول مطلوبة (دوال مجردة) وبعضها اختياري (دوال ملموسة/concrete methods). لا يمكنك إرسال النموذج نفسه - يجب عليك ملء جميع الحقول المطلوبة أولاً. وبالمثل، لا يمكنك إنشاء كائنات من فئة مجردة حتى يتم تنفيذ جميع الدوال المجردة في فئة فرعية.
استورد وحدة abc لإنشاء فئات مجردة (abstract classes):
from abc import ABC, abstractmethodقم بإنشاء فئة مجردة (abstract class) تحتوي على دوال مجردة (abstract methods):
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
def describe(self):
return "This is a shape" # دالة ملموسة (لها تنفيذ)يقوم المزخرف @abstractmethod بتمييز الأساليب التي يجب تنفيذها بواسطة الفئات الفرعية - هذه هي "الحقول المطلوبة" في نموذجك. الأساليب العادية مثل describe() يمكن أن تحتوي على عمليات تنفيذ ويتم توريثها كما هي - هذه هي "الحقول المعبأة مسبقاً".
لماذا نستخدم الأصناف المجردة (abstract classes)؟ إنها تفرض عقداً. إذا قمت بإنشاء صنف Shape، فأنت تقول "يجب أن يكون كل شكل قادراً على حساب مساحته ومحيطه". هذا يمنع الأخطاء البرمجية التي قد تحدث عندما تنسى تنفيذ الدوال (methods) الأساسية.
حاول إنشاء مثيل (instance) من الفئة المجردة (abstract class):
# سيؤدي هذا إلى حدوث خطأ:
# shape = Shape() # TypeError: لا يمكن إنشاء مثيل لفئة مجردةهذا الخطأ مقصود - فالأمر يشبه محاولة إرسال نموذج فارغ. تقول لغة Python "لم تقم بملء الحقول المطلوبة (طرق area و perimeter) بعد!"
أنشئ فئة فرعية ملموسة (concrete subclass) تقوم بتنفيذ جميع الدوال المجردة (abstract methods):
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
def perimeter(self):
return 2 * 3.14 * self.radiusالآن قمنا بـ "ملء جميع الحقول المطلوبة" - لقد قمنا بتنفيذ كل من area() و perimeter(). فئة Circle الآن هي فئة ملموسة كاملة يمكن إنشاء كائنات منها (instantiated).
الآن يمكنك إنشاء مثيلات من الفئة الملموسة (concrete class):
circle = Circle(5)
print(circle.area())
print(circle.perimeter())
print(circle.describe()) # ميثود ملموسة موروثةقم بإنشاء فئة فرعية ملموسة (concrete subclass) أخرى:
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width self.height
def perimeter(self):
return 2 * (self.width + self.height)
rectangle = Rectangle(4, 6)
print(rectangle.area())
print(rectangle.perimeter())المخرجات:
78.5
31.400000000000002
This is a shape
24
20تحدد الفئات المجردة (Abstract classes) عقداً يجب على الفئات الفرعية (subclasses) اتباعه. استخدم ABC و @abstractmethod لإنشاء فئات مجردة. يجب على الفئات الفرعية تنفيذ جميع الأساليب المجردة (abstract methods) وإلا فستكون هي أيضاً مجردة. يضمن ذلك الاتساق ويمنع عمليات التنفيذ غير المكتملة.
التحدي
متوسطفي هذا التحدي، ستقوم بتنفيذ نظام لمعالجة المدفوعات.
تحتاج إلى إكمال التنفيذ في هذه الملفات:
paymentmethod.py- فئة أساسية مجردة مع منطق التحقق من الصحةcreditcard.py- تنفيذ الدفع ببطاقة الائتمانpaypal.py- تنفيذ الدفع عبر PayPal
اتبع تعليقات TODO في كل ملف لتنفيذ الوظائف المطلوبة. ستوجهك التعليقات خطوة بخطوة لإنشاء الأساليب المجردة، وتنفيذ الفئات الفرعية الملموسة، وإنشاء علاقات استيراد صحيحة بين الملفات.
يحتوي ملف driver.py على حالات اختبار ستتحقق من أن تنفيذك يعمل بشكل صحيح، حيث يعالج سيناريوهات دفع مختلفة ويعرض التفاصيل المناسبة لكل وسيلة دفع.
ورقة مرجعية
لا يمكن إنشاء كائنات (instantiated) من الفئات المجردة (Abstract classes) بشكل مباشر، وهي تحتوي على دوال مجردة (abstract methods) يجب تنفيذها بواسطة الفئات الفرعية (subclasses).
استيراد وحدة abc:
from abc import ABC, abstractmethodإنشاء فئة مجردة تحتوي على دوال مجردة:
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
def describe(self):
return "This is a shape" # دالة ملموسة (لها تنفيذ)يقوم المزخرف (decorator) @abstractmethod بتمييز الدوال التي يجب تنفيذها بواسطة الفئات الفرعية. يمكن أن يكون للدوال العادية تنفيذ خاص بها.
لا يمكن إنشاء كائنات من الفئات المجردة:
# سيؤدي هذا إلى حدوث خطأ:
# shape = Shape() # TypeError: Can't instantiate abstract classإنشاء فئات فرعية ملموسة (concrete subclasses) تقوم بتنفيذ جميع الدوال المجردة:
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
def perimeter(self):
return 2 * 3.14 * self.radius
circle = Circle(5)
print(circle.area()) # 78.5
print(circle.describe()) # دالة ملموسة موروثةنقطة رئيسية: تحدد الفئات المجردة نموذجاً يجب على الفئات الفرعية اتباعه. يجب على الفئات الفرعية تنفيذ جميع الدوال المجردة، وإلا فستعتبر هي أيضاً فئات مجردة.
جرّب بنفسك
from creditcard import CreditCard
from paypal import PayPal
# اختبار التنفيذات - لا تقم بتعديل كود الاختبار هذا
cc = CreditCard("1234567890123456")
pp = PayPal("user@example.com")
# معالجة المدفوعات الصالحة
if cc.validate(100):
print(cc.process_payment(100))
print(cc.payment_details())
if pp.validate(200):
print(pp.process_payment(200))
print(pp.payment_details())
# تجربة مبلغ غير صالح
if not pp.validate(0):
print("Invalid payment amount")
# سيؤدي هذا إلى حدوث خطأ إذا تم إلغاء التعليق:
# pm = PaymentMethod() # لا يمكن إنشاء كائن من فئة مجردةيتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال