• 0
anasalama

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

سؤال

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

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

ومشكورين

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

11 إجابة على هذا السؤال .

  • 0

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

لكي تفهم الشرح, لازم تكون فاهم الوراثة 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 معينة لذلك. (هذا الكلام مختصر شوية و ربما غير دقيق تماما)

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

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

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

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

طيب, انت اول شي تعرف الـ inheritance؟

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

نعم

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

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

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

تم تعديل بواسطه الشـمري
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم

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

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

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

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

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

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

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

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

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

مع تحياتي

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

karimosh

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

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

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

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

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

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

من عيوني

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

السلام عليكم

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

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

#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[i] = pHorse;
}
cout << "\n";
for (i=0; i<5; i++)
{
 Ranch[i]->Fly();
 delete Ranch[i];
}
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.

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

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

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

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

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

توقعت ..

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

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

الواجهات ..

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

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

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

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

والان عرفت :(

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
زوار
This topic is now closed to further replies.

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .