ما هو تعدد الأشكال Polymorphism؟
يعد تعدد الأشكال جزءًا من “نظرية النوع” الرياضية، وتُستخدم كلمة تعدد الأشكال أيضاً في سياقات مختلفة، إذ تصف المواقف التي تحدث فيها شيء ما في عدة أشكال مختلفة، وفي البرمجة، الكائن (object) متعدد الأشكال هو الكائن القادر على اتخاذ أشكال متعددة، حيث يُعرف تعدد الأشكال بإمكانية الوصول إلى كائنات (objects) من أنواع مختلفة من خلال نفس الواجهة، ويمكن لكل نوع توفير الكود المستقل الخاص به لهذه الواجهة، ويعتمد نوع تعدد الأشكال الذي يخضع له الكائن على الوقت الذي يأخذ فيه الكائن شكله، وعلى أي جزء من الكائن يتم تحويله.
متى يتم تحويل الكائن؟
- متحرك (Dynamic).
ما الذي يتم تحويله؟
- دالة الكائن (Method).
- الكائن (Object).
تعدد الأشكال في البرمجة:
في لغات البرمجة ونظرية النوع، تعدد الأشكال هو توفير واجهة واحدة لكيانات من أنواع مختلفة، أو استخدام رمز واحد لتمثيل أنواع مختلفة ومتعددة. وتعدد الأشكال أحد المفاهيم الأساسية للبرمجة الشيئية (OOP)، ويتم تعريف الكائنات على أنها فئات (classes)، حيث يمكن أن يكون لديهم خصائص وطرق على سبيل المثال، يمكننا إنشاء كائن محدد على أنه الكلاس “Car”، ويمكن أن تحتوي هذه السيارة على:
- الخصائص: مثل اللون والطراز والسرعة الحالية.
- الطرق (وهي دوال يستدعيها الكلاس): مثل انطلق، توقف، انعطف يسارًا، انعطف يمينًا.
لكي يكون كائن السيارة أعلاه متعدد الأشكال، يمكن استدعاء خصائصه وطرقه باستخدام نفس اسم الكلاس “Car”، ولكن يتم استدعاء أنواع مختلفة اعتمادًا على كيفية استخدامه في الكود.
أنواع تعدد الأشكال:
هناك أربعة أنواع رئيسية من تعدد الأشكال، دعونا نلقي نظرة على كل واحدة:
تعدد الأشكال الفرعي (وقت التشغيل):
تعدد الأشكال الفرعي هو النوع الأكثر شيوعًا من تعدد الأشكال، وعادةً ما يكون مشار لهذا النوع عندما يقول شخص ما “الكائن متعدد الأشكال”، حيث يستخدم تعدد الأشكال الفرعي اسم كلاس واحد للإشارة إلى أنواع متعددة من الأنواع الفرعية في وقت واحد، في مثال السيارة أعلاه، قد قمنا بإنشاء كلاس “Car”، وتحديد كلاسات فرعية (subclasses) متعددة منه: (Ford و Chevrolet و Kia و Nissan و BMW و Tesla)، وكل سيارة لها لون خاص بها.
يمكن الإشارة لجميع الأنواع الفرعية بالتبادل باستخدام فئة “Car”، كل سيارة لها خاصية الون، والآن يمكننا الحصول على اللون كل نوع فرعي من كلاس السيارات، فإن تعدد الأشكال يحدث في وقت التشغيل، ويمكننا كتابة دالة لجلب لون أي من أنواع الكلاسات الفرعية للسيارة، قد تبدو الدالة على النحو التالي (غير مكتوبة بأي لغة برمجة محددة):
getColor(BMW)→ returns red getColor(Audi)→ returns blue getColor(Kia)→ returns yellow
تعدد الأشكال البارامترية (التحميل الزائد Overloading):
يوفر تعدد الأشكال البارامتري على وجه التحديد طريقة لاستخدام دالة واحدة (نفس الكود) للتفاعل مع أنواع متعددة من الكلاسات، من الأمثلة على ذلك، قائمة من أنواع البيانات، إذ يمكن لتعدد الأشكال البارامتي إزالة عناصر من القائمة، أو إضافة عناصر أو استبدالها بغض النظر عن نوع العنصر، والكود التالي هو دالة حدودية متعددة الأشكال مكتوبة بلغة بايثون:
for(element in list): list.remove(element)
تعدد الأشكال المخصص (وقت الترجمة):
بالنسبة لتعدد الأشكال المخصص، تعمل الدوال التي تحمل نفس الاسم بشكل مختلف لأنواع مختلفة، يتم كتابة لغة (Python) ديناميكيًا (لا يتطلب تحديد النوع)، دالة إضافة “+”، في لغة (Python) هي متعددة الأشكال مخصص؛ لأنها وظيفة واحدة بنفس الاسم، لكنها تعمل على أنواع متعددة، كما في المثال (Python):
3+4"Foot"+"ball"
وسيتم إرجاع قيمة من الأنواع المقابلة لها (int و String)، على التوالي:
→7→"Football"
بالنسبة لقيم النوع (int)، تضيف وظيفة الإضافة القيمتين معًا، لذا فإن (3 + 4) تُرجع (7)، بالنسبة لقيم النوع (String)، تقوم وظيفة الإضافة بربط السلسلتين، لذا فإن (“Foot”+“ball”) ترجع، “Football”.
تعدد الأشكال القسري (الصب Casting):
تعدد الأشكال القسري هو التحول المباشر من نوع إلى آخر، ويحدث ذلك عندما يتم تحويل نوع إلى نوع آخر، قد تحدثنا عن تعدد الأشكال من خلال التفاعل مع الأنواع المختلفة من خلال الكلاسات الفرعية أو الوظائف، وتحدثنا أيضاً عن إمكانية تحديد نوع الكائن عند تشغيل البرنامج، يمكن أن تعمل دالة واحدة مع أنواع مختلفة، مثال بسيط هو تحويل قيم الأرقام من (ints) إلى (doubles) أو إلى (floats)، والعكس صحيح اعتمادًا على لغة البرمجة، إما أن يُحدد النوع بناءً على المتغير، أو في بعض الأحيان توجد طريقة لتحويل الأنواع من قيمتها إلى نوع آخر، المثال التالي سيحول أنواعًا بلغة (Python):
int(43.2)#Converts a double to an intfloat(45)#Converts an int to a float
في لغة (Java)، قد تقوم بتحويل النوع ببساطة عن طريق تحديد النوع:
int num =23;double dnum = num;
أو:
double dnum =Double.valueOf(inum);