تعددية الأشكال (Polymorphism): المرونة القصوى في كتابة الأكواد


ماذا سنتعلم اليوم؟ سنغوص في مفهوم تعددية الأشكال (Polymorphism) في بايثون، وكيف تمكننا من كتابة أكواد أكثر مرونة وقابلية للتوسع وإعادة الاستخدام.

سنتعلم كيفية بناء أنظمة قادرة على التعامل مع أنواع مختلفة من الكائنات بطريقة موحدة.

الخطوة 1: تعددية الأشكال مع الدوال (Function Polymorphism)

تعددية الأشكال في أبسط صورها تعني أن دالة واحدة يمكنها العمل مع أنواع مختلفة من الكائنات، طالما أن هذه الكائنات توفر الواجهة (الأسماء والطرق) التي تتوقعها الدالة. في بايثون، هذا يتم بشكل طبيعي بفضل Duck Typing.

ملاحظة تقنية: Duck Typing تعني "إذا كان يمشي كالبطة ويصدر صوت البطة، فهو بطة". أي أننا لا نهتم بنوع الكائن بقدر اهتمامنا بالطرق (Methods) والخصائص (Attributes) التي يوفرها.

لنبدأ بمثال بسيط يوضح كيف يمكن لدالة واحدة استدعاء طريقة مشتركة من كائنات مختلفة.

# تعريف فئة الكلب
class Dog:
    def speak(self):
        return "Woof!" # الكلب ينبح

# تعريف فئة القط
class Cat:
    def speak(self):
        return "Meow!" # القط يموء

# دالة عامة تستقبل أي كائن لديه طريقة 'speak'
def animal_sound(animal):
    return animal.speak() # استدعاء طريقة 'speak' بغض النظر عن نوع الكائن

# إنشاء كائنات من الفئات المختلفة
my_dog = Dog()
my_cat = Cat()

# استخدام الدالة العامة مع الكائنات المختلفة
print(f"صوت الكلب: {animal_sound(my_dog)}")
print(f"صوت القط: {animal_sound(my_cat)}")

في هذا الجزء، قمنا بتعريف فئتين (Dog و Cat)، وكلتاهما تحتويان على طريقة speak(). ثم أنشأنا دالة animal_sound() التي تستقبل أي كائن وتستدعي طريقة speak() الخاصة به. هذا يوضح المرونة في التعامل مع الكائنات المختلفة بواجهة موحدة.

الخطوة 2: تعددية الأشكال مع الوراثة (Inheritance Polymorphism)

تظهر تعددية الأشكال بشكل قوي جداً عند استخدامها مع الوراثة. يمكننا تعريف فئة أساسية (Base Class) تحدد واجهة عامة، ثم فئات مشتقة (Derived Classes) تقوم بتنفيذ هذه الواجهة بطرقها الخاصة.

# تعريف الفئة الأساسية (الأب)
class Animal:
    def make_sound(self):
        raise NotImplementedError("يجب على الفئات المشتقة تنفيذ هذه الطريقة") # إجبار الفئات الفرعية على تنفيذها

# تعريف فئة الكلب التي ترث من Animal
class Dog(Animal):
    def make_sound(self):
        return "Woof!" # تنفيذ خاص للكلب

# تعريف فئة القط التي ترث من Animal
class Cat(Animal):
    def make_sound(self):
        return "Meow!" # تنفيذ خاص للقط

# تعريف فئة البقرة التي ترث من Animal
class Cow(Animal):
    def make_sound(self):
        return "Moo!" # تنفيذ خاص للبقرة

# دالة يمكنها التعامل مع أي كائن من نوع Animal أو يرث منه
def describe_animal(animal):
    return f"هذا الحيوان يصدر الصوت: {animal.make_sound()}" # استدعاء الطريقة المتجاوزة

# إنشاء قائمة من كائنات الحيوانات
animals = [Dog(), Cat(), Cow()]

# المرور على القائمة واستدعاء الدالة
for animal in animals:
    print(describe_animal(animal))

هنا، Animal هي الفئة الأساسية التي تعرف الطريقة make_sound(). كل من Dog و Cat و Cow ترث من Animal وتوفر تنفيذها الخاص للطريقة make_sound(). هذا يسمح لنا بمعاملة جميع هذه الكائنات كـ Animal، بينما لا يزال كل منها يتصرف بطريقته الخاصة عند استدعاء make_sound().

الخطوة 3: تعددية الأشكال مع أنواع البيانات المدمجة (Built-in Type Polymorphism)

بايثون نفسها تستخدم تعددية الأشكال بشكل مكثف. العديد من الدوال والعمليات تعمل بشكل مختلف بناءً على نوع البيانات التي تتعامل معها. على سبيل المثال، عملية الجمع (+) تتصرف بشكل مختلف مع الأرقام وسلاسل الحروف والقوائم.

# تعددية الأشكال لعملية الجمع (+)
num1 = 5
num2 = 10
print(f"جمع الأرقام: {num1 + num2}") # عملية جمع حسابية

str1 = "Hello"
str2 = " World"
print(f"جمع السلاسل: {str1 + str2}") # عملية دمج سلاسل

list1 = [1, 2]
list2 = [3, 4]
print(f"جمع القوائم: {list1 + list2}") # عملية دمج قوائم

# تعددية الأشكال لدالة len()
my_string = "Python"
my_list = [1, 2, 3, 4, 5]
my_tuple = (10, 20, 30)

print(f"طول السلسلة '{my_string}': {len(my_string)}")
print(f"طول القائمة {my_list}: {len(my_list)}")
print(f"طول التوبل {my_tuple}: {len(my_tuple)}")

يوضح هذا الجزء كيف أن نفس المعامل (+) والدالة (len()) يمكن أن تنتج سلوكيات مختلفة تمامًا بناءً على نوع الكائنات التي يتم تطبيقها عليها. هذا مثال رائع على تعددية الأشكال المدمجة في لغة بايثون نفسها.

الكود النهائي الكامل

إليك الكود كاملاً الذي يجمع جميع الأمثلة التي ناقشناها أعلاه، مما يوفر نظرة شاملة على تعددية الأشكال في بايثون.

# --- الخطوة 1: تعددية الأشكال مع الدوال (Function Polymorphism) ---

# تعريف فئة الكلب
class Dog:
    def speak(self):
        return "Woof!" # الكلب ينبح

# تعريف فئة القط
class Cat:
    def speak(self):
        return "Meow!" # القط يموء

# دالة عامة تستقبل أي كائن لديه طريقة 'speak'
def animal_sound(animal):
    return animal.speak() # استدعاء طريقة 'speak' بغض النظر عن نوع الكائن

print("--- تعددية الأشكال مع الدوال ---")
my_dog = Dog()
my_cat = Cat()
print(f"صوت الكلب: {animal_sound(my_dog)}")
print(f"صوت القط: {animal_sound(my_cat)}")
print("-" * 30)

# --- الخطوة 2: تعددية الأشكال مع الوراثة (Inheritance Polymorphism) ---

# تعريف الفئة الأساسية (الأب)
class Animal:
    def make_sound(self):
        raise NotImplementedError("يجب على الفئات المشتقة تنفيذ هذه الطريقة") # إجبار الفئات الفرعية على تنفيذها

# تعريف فئة الكلب التي ترث من Animal (نستخدم Canine لتجنب تضارب الأسماء مع فئة Dog الأولى)
class Canine(Animal):
    def make_sound(self):
        return "Woof!" # تنفيذ خاص للكلب

# تعريف فئة القط التي ترث من Animal (نستخدم Feline لتجنب تضارب الأسماء مع فئة Cat الأولى)
class Feline(Animal):
    def make_sound(self):
        return "Meow!" # تنفيذ خاص للقط

# تعريف فئة البقرة التي ترث من Animal (نستخدم Bovine لتجنب تضارب الأسماء مع فئة Cow الأولى)
class Bovine(Animal):
    def make_sound(self):
        return "Moo!" # تنفيذ خاص للبقرة

# دالة يمكنها التعامل مع أي كائن من نوع Animal أو يرث منه
def describe_animal(animal):
    return f"هذا الحيوان يصدر الصوت: {animal.make_sound()}" # استدعاء الطريقة المتجاوزة

print("--- تعددية الأشكال مع الوراثة ---")
animals_inherited = [Canine(), Feline(), Bovine()] # قائمة بكائنات من الفئات المشتقة

for animal in animals_inherited:
    print(describe_animal(animal))
print("-" * 30)

# --- الخطوة 3: تعددية الأشكال مع أنواع البيانات المدمجة (Built-in Type Polymorphism) ---

print("--- تعددية الأشكال مع أنواع البيانات المدمجة ---")
# تعددية الأشكال لعملية الجمع (+)
num1 = 5
num2 = 10
print(f"جمع الأرقام: {num1 + num2}") # عملية جمع حسابية

str1 = "Hello"
str2 = " World"
print(f"جمع السلاسل: {str1 + str2}") # عملية دمج سلاسل

list1 = [1, 2]
list2 = [3, 4]
print(f"جمع القوائم: {list1 + list2}") # عملية دمج قوائم

# تعددية الأشكال لدالة len()
my_string = "Python"
my_list = [1, 2, 3, 4, 5]
my_tuple = (10, 20, 30)

print(f"طول السلسلة '{my_string}': {len(my_string)}")
print(f"طول القائمة {my_list}: {len(my_list)}")
print(f"طول التوبل {my_tuple}: {len(my_tuple)}")
print("-" * 30)

النتيجة المتوقعة

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

--- تعددية الأشكال مع الدوال ---
صوت الكلب: Woof!
صوت القط: Meow!
------------------------------
--- تعددية الأشكال مع الوراثة ---
هذا الحيوان يصدر الصوت: Woof!
هذا الحيوان يصدر الصوت: Meow!
هذا الحيوان يصدر الصوت: Moo!
------------------------------
--- تعددية الأشكال مع أنواع البيانات المدمجة ---
جمع الأرقام: 15
جمع السلاسل: Hello World
جمع القوائم: [1, 2, 3, 4]
طول السلسلة 'Python': 6
طول القائمة [1, 2, 3, 4, 5]: 5
طول التوبل (10, 20, 30): 3
------------------------------