الدرس السادس

تعميق مفاهيم البرمجة كائنية التوجه

في هذا الدرس سنتعمق في مفاهيم OOP المتقدمة: أنواع الوراثة، تعدد الأشكال المتقدم (Duck Typing)، التغليف باستخدام @property، والتجريد الكامل باستخدام ABC. هذه المفاهيم أساسية لبناء تطبيقات احترافية.

المفاهيم المتقدمة في OOP

في الدرس السابق تعرفنا على أساسيات البرمجة الكائنية. الآن سنتعمق في مفاهيم متقدمة تستخدمها الشركات الكبرى في بناء أنظمتها البرمجية.

🎯 ما ستتعلمه في هذا الدرس

1. أنواع الوراثة: Single, Multiple, Multilevel, Hierarchical, Hybrid

2. Duck Typing: مبدأ "إذا كان يمشي كالبطة..."

3. التغليف الاحترافي: استخدام @property بدلاً من getter/setter

4. التجريد: ABC وفرض تنفيذ الدوال في الفئات الفرعية

1. أنواع الوراثة (Types of Inheritance)

بايثون تدعم عدة أشكال هيكلية للوراثة تتيح تصميم علاقات معقدة بين الفئات.

الأنواع الرئيسية:

  • الوراثة الفردية (Single): فئة فرعية ترث من فئة أب واحدة
  • الوراثة المتعددة (Multiple): فئة فرعية ترث من أكثر من فئة أب
  • الوراثة متعددة المستويات (Multilevel): سلسلة وراثة (جد ← أب ← حفيد)
  • الوراثة الهرمية (Hierarchical): عدة فئات فرعية ترث من نفس الأب
  • الوراثة الهجينة (Hybrid): مزيج من نوعين أو أكثر
Vehicle (الجد)
Car (الأب)
SportsCar (الحفيد)

Multilevel Inheritance - الوراثة متعددة المستويات

مثال: الوراثة متعددة المستويات (Multilevel Inheritance)

# الجد (Grandparent)
class Vehicle:
    def start(self):
        print("Vehicle started")

# الأب (Parent) - يرث من الجد
class Car(Vehicle):
    def drive(self):
        print("Car is driving")

# الحفيد (Child) - يرث من الأب، وبالتالي يمتلك خصائص الجد أيضاً
class SportsCar(Car):
    def race(self):
        print("Racing fast!")

# إنشاء كائن من الحفيد
my_car = SportsCar()
my_car.start()  # من الجد Vehicle
my_car.drive()  # من الأب Car
my_car.race()   # من SportsCar نفسه
# أسلوب المحترفين: استخدام Mixins للوراثة المتعددة المنظمة
class EngineMixin:
    """Mixin class just for engine functionality."""
    def start_engine(self):
        print("Engine roared to life.")

class NavigationMixin:
    """Mixin class just for navigation."""
    def set_route(self, destination: str):
        print(f"Route set to {destination}.")

class SportsCar(EngineMixin, NavigationMixin):
    """Combines features from multiple mixins."""
    def drive(self):
        self.start_engine()
        self.set_route("Track")
        print("Driving...")

car = SportsCar()
car.drive()
🔑 Mixins: هي فئات صغيرة تضيف وظيفة واحدة محددة. المحترفون يستخدمونها لتجنب تعقيد الوراثة المتعددة وجعل الكود أكثر قابلية للصيانة.

2. تعميق تعدد الأشكال (Advanced Polymorphism)

تعدد الأشكال يسمح بتنفيذ نفس العملية بسلوكيات مختلفة. في بايثون، هناك مفهوم قوي اسمه Duck Typing.

مبدأ Duck Typing

🦆 "إذا كان يمشي كالبطة ويصدر صوت البطة، فهو بطة"

في بايثون، نهتم بوجود الدالة في الكائن أكثر من نوع الكائن نفسه.

# المبتدئ قد يحاول التحقق من النوع (غير مرغوب في بايثون)
class Bird:
    def fly(self):
        print("Flying high")

class Airplane:
    def fly(self):
        print("Taking off")

def make_it_fly(obj):
    # التحقق من النوع - طريقة غير بايثونية
    if isinstance(obj, Bird):
        obj.fly()
    elif isinstance(obj, Airplane):
        obj.fly()

bird = Bird()
make_it_fly(bird)
# أسلوب المحترفين: Duck Typing الحقيقي
class Bird:
    def fly(self):
        print("Bird is flying naturally.")

class Airplane:
    def fly(self):
        print("Airplane is taking off mechanically.")

def perform_flight(entity):
    """
    Accepts ANY object that has a 'fly' method.
    We don't care about the class type.
    """
    if hasattr(entity, 'fly') and callable(entity.fly):
        entity.fly()
    else:
        print("This object cannot fly.")

# يعمل مع أي كائن يحقق الشرط
perform_flight(Bird())
perform_flight(Airplane())
💡 لماذا Duck Typing أفضل؟
• يجعل الكود أكثر مرونة
• يسمح بإضافة فئات جديدة دون تعديل الكود القديم
• هذا هو الأسلوب البايثوني الحقيقي (Pythonic Way)

3. تفاصيل التغليف (Deep Encapsulation)

التغليف ليس مجرد وضع البيانات في فئة، بل التحكم في الوصول إليها عبر مستويات الحماية:

المستوى الصيغة الوصف
Public (عام) name متاح للجميع من أي مكان
Protected (محمي) _name إشارة للمبرمجين: للاستخدام الداخلي أو للفئات الفرعية فقط
Private (خاص) __name لا يمكن الوصول إليه من خارج الفئة (Name Mangling)

استخدام @property (الطريقة الاحترافية)

# طريقة Getter/Setter التقليدية (أسلوب قديم)
class Dog:
    def __init__(self, age):
        self._age = age  # متغير محمي

    def get_age(self):
        return self._age

    def set_age(self, new_age):
        if new_age > 0:
            self._age = new_age
        else:
            print("Invalid age!")

d = Dog(5)
d.set_age(6)
print(d.get_age())  # 6
# أسلوب المحترفين: Pythonic Way - @property
class Dog:
    def __init__(self, age: int):
        self.__age = age  # Private member

    @property  # الـ Getter
    def age(self):
        """The getter for age."""
        return self.__age

    @age.setter  # الـ Setter مع التحقق من الصحة
    def age(self, value):
        """The setter logic with validation."""
        if value <= 0:
            raise ValueError("Age must be positive!")
        self.__age = value

d = Dog(5)
d.age = 10  # يبدو كأنه تعيين مباشر، لكنه يمر عبر setter
print(f"Dog age is {d.age}")  # 10

🔐 لماذا @property أفضل؟

✅ يبدو طبيعياً مثل الوصول للمتغير العادي

✅ يتيح إضافة منطق التحقق دون تغيير واجهة الاستخدام

✅ هذا هو الأسلوب البايثوني الموصى به (PEP 8)

4. التجريد الكامل (Abstraction)

التجريد يركز على "ماذا يفعل الكائن" ويخفي "كيف يفعل ذلك". يتم تحقيقه باستخدام الفئات المجردة (Abstract Base Classes - ABC).

📌 مفاهيم مهمة:
طريقة مجردة (Abstract Method): دالة يتم تعريفها في الأب ولكن يجب كتابة كودها في الفئة الفرعية
التجريد الجزئي: الفئة تحتوي دوال مجردة ودوال عادية
التجريد الكامل: الفئة تحتوي فقط على دوال مجردة (شبه الواجهة Interface)
# محاولة المحاكاة بدون مكتبات (غير فعالة)
class Animal:
    def sound(self):
        # ترك الدالة فارغة على أمل أن يقوم الابن بتعديلها
        pass

class Cat(Animal):
    def sound(self):
        print("Meow")

c = Cat()
c.sound()  # Meow
# أسلوب المحترفين: استخدام ABC لإجبار التنفيذ
from abc import ABC, abstractmethod

class Animal(ABC):
    """Abstract Base Class"""

    @abstractmethod
    def sound(self):
        """Subclasses MUST implement this."""
        pass

    def sleep(self):
        """Concrete method (shared behavior)."""
        print("Zzzzz...")

class Cat(Animal):
    def sound(self):
        return "Meow"

# class Dog(Animal): pass
# سيؤدي السطر أعلاه لخطأ لأننا لم ننفذ دالة sound
# TypeError: Can't instantiate abstract class Dog

cat = Cat()
print(cat.sound())  # Meow
cat.sleep()  # Zzzzz...

🎯 فوائد استخدام ABC

إجبار التنفيذ: بايثون لن تسمح بإنشاء كائن من فئة لم تنفذ الدوال المجردة

توثيق ذاتي: الكود يوضح بنفسه ماذا يجب على الفئات الفرعية تنفيذه

تقليل الأخطاء: اكتشاف المشاكل عند الكتابة بدلاً من وقت التشغيل

معيار صناعي: تستخدمه المكتبات الكبيرة مثل Django و Flask

Ad Space (Adaptive)