تعميق مفاهيم البرمجة كائنية التوجه
في هذا الدرس سنتعمق في مفاهيم 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): مزيج من نوعين أو أكثر
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()
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())
• يجعل الكود أكثر مرونة
• يسمح بإضافة فئات جديدة دون تعديل الكود القديم
• هذا هو الأسلوب البايثوني الحقيقي (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