الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

ما هو الPolymorphism في البرمجة الكائنية

مغلق
بدأه anasalama في 9 مايو 2005 · 11 رد · 11,004 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم ورحمة الله وبركاته

أرجو ممن يستطيع شرح لي مفهوم الـPolymorphism في البرمجة الكائنية المنحى OOP ألا يبخل علي فقد قرأت الكثير في هذا الصدد ولكني لم أتوصل لتكوين فكرة واضحة .

ومشكورين

أنــس عــمــار

#2

هذا شرح كنت قد حاولت كتابته منذ فترة .. لا ادري اذا كان جيدا ام لا, و لكن جرب قرائته, و قلي اذا كان مفيدا ام لا.

لكي تفهم الشرح, لازم تكون فاهم الوراثة inheritance و المؤشرات pointers

---------

اول شي نحتاج ان ننعرفه لفهم البوليمورفزم انك تستطيع ان تجعل مؤشرا للكلاس الأب يؤشر الى كائن من كلاس مشتق عنه.

مثلا لو كان عندنا class CShape و فئة مشتقة عنها class CCircle : public CShape فنستطيع عمل الآتي:

CShape *shape = new CCircle();

و هذا منطقي .. فالدائرة هي في النهاية شكل .. ولا بد ان نستطيع ان نشير اليها على أنها شكل

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

في اللعبة مثلا .. كل شي متحرك (و حتى بعض الاشياء غير المتحركة) هو عبارة عن entity ففي تعريف اللعبة, كل شيء له خصائص مشتركة و هي: ان هناك model يجب تحميله لعرض الشيء .. هذا المودل له animations و كل شيء يجب ان يتم تحديثه مرة في كل أطار frame.

فنستطيع ربط كل شيء في اللعبة بسلسلة و نستخدم لأجل ذلك مؤشرات لـ entities فبذلك لا نحتاج للقلق عن تفاصيل كل مؤشر.

الأمر الآخر .. و هو الأهم: نستطيع تعريف method في الكلاس الرئيسي ثم نعيد تعريف المثد في الكلاس الفرعي و لكن نغير طريقة تنفيذه .. في هذه الحالة نستطيع تعريف ميثود معين مثل Draw و كل نوع من الأشكال (كل كلاس فرعي) يعيد تعريف هذا الميثود حسب الكلاس .. فالدائرة ستقوم برسم دائرة .. و المربع يقوم برسم مربع .. و الملث يرسم مثلث .. الخ.

الآن .. هنا بيت القصيد, عندما يكون عندك بوينتر من الكلاس الأساسي يشير الى كائن من كلاس فرعي, و تستدعي الميثود Draw فإن الفنكشن اللذي سينفذ لن يكون CShape::Draw بل سيكون الـ Draw الخاص بالكائن المعني بالشأن .. فلو كان لديك مؤشر CShape يشير الى كائن CCircle كما في المثال السابق .. فإن السطر:

shape->Draw();

سيقوم بتنفيذ CCircle::Draw

قد تقول كيف يعرف الكومبايلر ذلك؟ الحقيقة ان الكومبايلر لا يعرف ذلك .. فهذه العملية تتم اثناء تنفيذ البرنامج.

يمعنى .. ممكن يكون عندك قائمة مترابطة بعشرة أشكال .. لكن كل شكل هو من نوع مختلف عن الآخر (واحد مثلث و الاخر دائة و آخر مربع .. الخ) و قمت بعمل loop على القائمة بحيث تمر على كل عنصر و تستدعي Draw على ذلك الكائن باستخدام مؤشر للكلاس الرئيسي .. فإن كل عنصر من هذه العناصر سينادي الفنكشن الخاص به ..

طبعا لما يكون عندك حلقة تكرارية loop فإنك ستكتب السطر shape->Draw() مرة واحدة .. لكن في كل حلقة من حلقات اللووب ستستدعي فنكشن مختلف عن الآخر .. اي فنكشن سيتم استدعائه؟ هذا يتم تحديدها أثناء تشغيل البرنامج.

(على فكرة .. لهذا اسمه method .. لأنه تعريف طريقة تنفيذ مهمة معينة لكنه يختلف من كلاس الى آخر)

فهذه هي البولي مورفزم: انك عندك فنكشن معرف عدة مرات .. و عندما تكتب سطرا في البرنامج يستدعي هذا الفنكشن .. فأنت لا تعرف بالظبط أثناء كتابة البرنامج أي نسخة من الفنكشن سيتم استدعائها .. فهذا يتم تحديده اثناء تنفيذ البرنامج .. يعني at run time.

هناك تطبيقات مهمة جدا لهذه الخاصية .. و هي مستخدمة بكثرة في الالعاب, فكل entity في اللعبة موجودة في سلسلة مترابطة و في كل إطار frame يتم المرور على كل هذه الكائنات و تحديثها عن طريق استدعاء methods معينة لذلك. (هذا الكلام مختصر شوية و ربما غير دقيق تماما)

#3

لا ادري هل الشرح مفهوم ام معقد؟

اتمنى اي واحد قرأه ان يخبرني عن مدى وضوح الشرح او انه مبهم .. هل مثلا واحد قرأه و فهم الموضوع؟ اما زاد تعقيدا؟ ام لم يفهم شي اصلا؟

#4

لا الشرح ممتاز و مبسط للغاية

Sr. Software Development Engineer
Hulu, LLC
My Blogs

#5

انا يا اخ حسن مافهمت شي .. اسف ..

اللي اعرفه عن تعدد الواجهات هو :

عندك سيارة تمشي .. وحصان يمشي .. كلهم يمشون لكن مشي الحصان ليس كمشي السيارة ...

هذا الشرح لقيته بأحد المواقع العربية :)

اتمنى يا اخ حسن انك تعطي مثال اساسي .. وتبدا تعلق عليه .. او على راحتك المهم شرح زين

واسف على كثرة الكلام ...

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#7

نعم

اعرف جزء جيد جدا من الوراثة ..

ولكن احب اقول اني يمكن بالغت قليلا لما قلت ما فهمت شي ..

انا فهمت كمعلومات عامة وفهمت بعض مفاهيم تعدد الواجهات ... وكله فقط من شرحك لكن لازلت لا اعرف كيف استخدمه ومتى ..

تم تعديل هذه المشاركة بواسطة الشـمري في 20 مايو 2005 في 00:45

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#8

السلام عليكم

أود ان اشرح ولكن اعذروني اذا لم افصل كفاية

في البداية قبل ان نتعرف على ال Polymorphism أو تعدد الاشكال يجب ان نتعرف على نوع من الدوال يسمى الدوال التخيلية أو ال virtual functions

عندما نكتب دالة عضو في طبقة قاعدة ونعرف مقدما ان طبقة ما سوف نشتقها من الطبقة القاعدة وتحتوي على دالة بنفس الاسم ونفس المتغيرات المرسلة والمتغير الذي ترجع به الدالة لابد ان تكون الدالة العضو في الطبقة القاعدة من نوع virtual بالشكل:

virtual void Draw(){cout<<"drawing...";}

عندما لا نضع الكلمة virtual قبل اسم دالة طبقة القاعدة لا نستطيع تنفيذ تلك الدالة المعرفة للطبقة المشتقة باستدعائها من خلال مؤشر الى الطبقة الاب لان المترجم سوف ينفذ النسخة الخاصة بطبقة الاب. عند وضع دالة تخيلية في برنامج ما فان المترجم يخزن الدالة في موقع خاص بالذاكرة. ثم يتناول الدالة من خلال جدول مؤشرات.

تعدد الاشكال(polymorphism):

الدوال التخيلية هي مفتاح تعدد الاشكال فعندما نستدعي دالة عضو تخيلية من خلال مؤشر الى طبقة قاعدة فان المترجم ينفذ النسخة المعرفة في الطبقة المشتقة بدلا من استدعاء النسخة المعرفة للطبقة القاعدة. هذا معاكس تماما لما يحدث عند عدم استخدام الكلمة virtual .

عند كتابة دالة تخيلية لا يجبرنا المترجم على اعادة تعريفها في الطبقة المشتقة ولا يتسبب ذلك في مشكلة. مع ان الدوال التخيلية تعد ميزة قوية في لغة ++C الا اننا لا نستطيع تصميم جميع الدوال من النوع التخيلي. يرجع هذا لان استخدام الدوال التخيلية يسبب عبئا على البرنامج لان استخدام تلك الدوال يتم بطريقة غير مباشرة مما يؤدي الى بطء التنفيذ نوعا ما.

أتمنى أن أكون قد وضحت ولو بعض الشيء

مع تحياتي

#9

karimosh

مشكور جدا اخي .. امور كثير تعلمتها فقط من شرحك وهذه حقيقة دون مجاملة ..

لكن انت ماتعرفني زين ... اخوك الشمري طماااااع ... يعني نريد المزيد عن تعدد الواجهات ...

ويا ليت تحلي الدروس ببعض الاكواد :)

والكلام موجه الى جميع الاعضاء.. اللي عنده وقت + ما عليه اختبارات + فاضي شغل

ياليت يساعدنا ....

وعلى اقل من مهلكم ..

جزاكم الله خير .

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#10

من عيوني

لكن اعطيني شوية وقت

#11

السلام عليكم

أخي الشمري اليك هذا المثال وأرجو ان يوضح لك الصورة

لنفرض اننا انشانا طبقة قاعدة كما يلي:

#include <iostream>
using namespace std;
class Horse
{
public:
	void Gallop(){ cout << "Galloping...\n"; }
	virtual void Fly() { cout << "Horses can't fly.\n"; }
private:
	int itsAge;
};

لاحظ ان الدالة Fly عرفناها كـ virtual وهذا يعني اننا سنستخدمها(ليس بالضرورة) في الطبقات المشتقة

والان سنعرف طبقة مشتقة من الطبقة القاعدة:

class Pegasus : public Horse
{
public:
	virtual void Fly() { cout << "I can fly! I can fly! I can fly!\n"; }
};

في الطبقة المشتقة عرفنا نفس الدالة Fly ولكن وظيفتها تختلف عن وظيفة الدالة نفسها في الطبقة القاعدة وهذا ما قصدنا ان نفعله. سنرى لاحقا كيف سنميز بينهما

والان لنكتب الـ main الخاص بالبرنامج:

int main()
{
	Horse* Ranch[5];
	Horse* pHorse;
	int choice,i;
	for (i=0; i<5; i++)
	{
  cout << "(1)Horse (2)Pegasus: ";
  cin >> choice;
  if (choice == 2)
 	 pHorse = new Pegasus;
  else
 	 pHorse = new Horse;
  Ranch = pHorse;
	}
	cout << "\n";
	for (i=0; i<5; i++)
	{
  Ranch->Fly();
  delete Ranch;
	}
	return 0;
}

في الكود انشانا مصفوفة مؤشرات من الطبقة القاعدة Horse وفي الـloop بدانا بتعبئة المؤشرات حسب رغبة المستخدم لاحظ ان مؤشر على الطبقة القاعدة باستطاعتنا ان نجعله يؤشر على الطبقة المشتقة ولكن ليس العكس.

اذا نفذنا هذا البرنامج فاننا سنرى ما المقصود من استخدام الدالة التخيلية :

(1)Horse (2)Pegasus: 1
(1)Horse (2)Pegasus: 2
(1)Horse (2)Pegasus: 2
(1)Horse (2)Pegasus: 1
(1)Horse (2)Pegasus: 1

Horses can't fly.
I can fly! I can fly! I can fly!
I can fly! I can fly! I can fly!
Horses can't fly.
Horses can't fly.

أتمنى بهذا المثال ان اكون وضحت ما المقصود من الدوال التخيلية وتعدد الاشكال

مع تحياتي للجميع

#12

عندي كلمة واحدة اقدر اقولها : جزاك الله خير يا karimosh..

درس واضح ومفهوم .. تبين لي كثير من الامور الغامضة .. يبدو لي المسألة اهون مما

توقعت ..

ولا انسى ايضا اشكر اخونا حسن :: بصراحة كان درسه ايضا مفهوم .. لكن يحتاج منا

الى تركيز فقط .. قرأته اكثر من خمس مرات وبدأت استوعب المفهوم العام لتعدد

الواجهات ..

فعلا اصعب شي بالبرمجة هي تعلم المفاهيم الجديدة... كالمؤشرات والوراثة وتعدد

الواجهات ... (الـــــــــخ^33)..

اذكر كلمة لازالت تزن في اذني :) ..قالها مشرفنا هيثم : "" لاتقوم تعدد الواجهات الا

بوجود المؤشرات .. ""

والان عرفت :(

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

هذا الموضوع مغلق.

مواضيع مشابهة