Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الأصناف والدوال المجردة

جزء من قسم 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) وإلا فستكون هي أيضاً مجردة. يضمن ذلك الاتساق ويمنع عمليات التنفيذ غير المكتملة.

challenge icon

التحدي

متوسط

في هذا التحدي، ستقوم بتنفيذ نظام لمعالجة المدفوعات.

تحتاج إلى إكمال التنفيذ في هذه الملفات:

  • 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()  # لا يمكن إنشاء كائن من فئة مجردة
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming