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

Expand Your Oop Knowledge

مغلق
بدأه Robatic في 3 يناير 2008 · 2 رد · 2,087 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

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

كيف الحال يا أخواني الأعزاء

يا اخواني إن شاء الله في هذا الموضوع راح اتكلم ( إذا سمحتولي ) عن الـ OOP و شوية عن الـ Inheritance

وكلنا نعرف عنها، لكن راح اتطرق إلى الـ virtual functions بالأخص

بس قبل كل حاجة خلوني اعطيكم مقدمة بسيطة عن الـ Pointers to Objects

لو مثلا كان عندنا كلاس بسيط بإسم MyClass ، عشان نعمل منه كائن نكتب التالي

MyClass someObject

إلى الآن مافي شيء جديد، طيب لو نريد نعمل كائن ولكن مش أي كائن بل مؤشر، نكتب التالي

MyClass* obPtr = new MyClass()

إلى الآن الي عنده خلفية عن الـ Java او الـ C# ما عنده اي مشكلة

طيب نفرض إنه عندنا كلاسين Base و Derived حيث انه الثاني قام بوراثة الأول، هل العبارة التالية صحيحه ؟؟؟

Base* ptr = new Derived()

نعم ( ممكن البعض يتفاجأ ) ولكن خلونا نوضح اكثر، افرض الكلاسين الي تم ذكرهم فوق معرفين بالطريقة التالي :

class Base
{
public:
	void function() { cout << "This is from Base Class\n"; }
};

class Derived : public Base
{
public:
	void function() { cout << "This is from Derived Class\n"; }
};

ودالة الـ main معرفة على النحو التالي :

void main()
{
	Base *b = new Base();
	b->function();

	Derived *d = new Derived();
	d->function();
}

طبعا الـ output راح يكون كالتالي ( اوه كم مرة كتبت كلمة "التالي" ؟؟ :S )

This is from Base Class
This is from Derived Class

طيب لو أعدنا تعريف الـ main كما يلي ( لازم من التغيير؟؟؟!!!! ):

void main()
{
	Base *b = new Derived();
	b->function();
}

ماذا ستكون المخرجات ؟؟؟؟؟

This is from Base

همممممممم ؟؟ محير !!!!!

بكل بساطة على الرغم من إنه تم تعديل "Overload" للدالة function ولكن ما زالت تستخدم النسخة الموجود في Base Class بدلا من

النسخة الموجوده في Derived Class

لحل هذه المشكلة نقوم بتعريف الدالة function والموجوده في الـ Base على أنها virtual كالتالي

class Base
{
public:
	virtual void function() { cout << "This is from Base Class\n"; }
};

ثم نقوم بتنفيذ البرنامج.

طيب إش الفائدة من هذا الكلام أو كيف ممكن نسخر هذا الكلام لنا ؟؟؟؟

اقول : إذاذ كان عندنا كلاس A وكان في كلاس ثاني B والكلاس B ورث من الكلاس A فإن الكلاس A هو B والعكس غير صحيح

يعني نقدر نستخدم مؤشر من نوع A يأشر على B

خلونا نعدل في الكود تبع الـ Base والـ Derived كالتالي ونضيف دالة خارجية نرسلها Pointer من نوع Base وتقوم بتنفيذ الدالة function

ونشوف كيف تطلع النتيجة

#include <iostream>

using namespace std;

class Base
{
public:
	virtual void function() { cout << "This is from Base Class\n"; }
};

class Derived : public Base
{
public:
	void function() { cout << "This is from Derived Class\n"; }
};


void test(Base* anything) {anything->function();}


void main()
{
	Base* p = new Base();
	test(p);

	p = new Derived();
	test(p);
}

لو تلاحظوا ناتج المخرجات إنه تم تنفيذ الدالة على حسب مهي معرفة في كل كلاس مع إنه الدالة test ما تستقبل إلا pointer من نوع Base

بمعنى آخر إحنا قدرنا نعمم الدالة test على كل من Base & Derived.

كيف يفيدنا هذا التكتيك ؟؟

خذ السناريو التالي :

تخيل نفسك شغال في بنك كمبرمج، وقمت بكتابة برنامج الصراف الآلي ATM بحيث انه يستقبل حساب توفير وحساب جاري

وتحليلك كان كالتالي :

كتابة كلاس خاص بحساب جاري RegularAccount

كتابة كلاس خاص بحساب توفير SavingAccount

والفرق بينهم انه في الـ RegularAccount كان في دالة إسمها withdrawl وهي مسؤولة عن عملية السحب النقدي وتم عمل overload

لنفس الدالة في الـ SavingAccount بحيث إنه تختلف في حساب معدل الفائدة لكل من الحسابين

فبدون الـ virtual function راح يكون البرنامج تقريبا كالتالي ( نظريا):

تحديد نوع الكائن إذا كان RegularAccount أو SavingAccount

بحيث يتم عمل كائن جديد من نفس النوع ثم قم بتنفيذ الدالة withdrawl

طيب لو طلب منك المدير إضافة 1000 نوع حساب جديد كيف الحل ؟؟؟!!!!

على نفس المنطق المذكور أعلاه راح تقوم بكتابة 1000 IF و 1000 سطر لإنشاء كائن و 1000 إستدعاء للدالة withdrawl

هنا يجيء دور الـ Pure Abstract Class و الـ Virtual Functions

حيث إنه تقوم بعمل Abstract Class بإسم مثلا Account وفيه الدالة withdrawl معرفة على إنها virtual وبعد كذا قوم بتعريف كلاس لكل حساب ولكن قم بتوريثة للـ Account وقم بتغيير الدالة withdrawl على حسب ما يتناسب لكل نوع حساب

وبعد ذلك قوم بكتابة دالة عامة تقوم بإستدعاء الدالة withdrawl ولكن عن طريق pointer من نوع Account يتم إرساله لها على النحو التالي

void doWithdrawl(Account* ob) { ob->withdrawl(); }

بهذه الطريقة قدرنا نضمن إنه الدالة doWithdrawl راح تقوم بعملية السحب النقدي بشكل صحيح لكل حساب بغض النظر عن نوع الحساب

وهذا ما يسمى بالـ Generic Programming ونفس الأسلوب ينطبق على الـ Java & CSharp

إن شاء الله ما اكون لخبطكم وإن شاء الله يكون هذا الدرس مفيد وأتمنى لكم التوفيق

اخوكم Robatic

Happy Programming

#2

اخي Robatic ..

لماذا تقول متواضع ..

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

موضوع جميل جدا .. وفيه معلومات وفيره عن هذا الجزء المهم من الOOP ..

تحياتي العطرة .. .

يَارَبُ إِن ضَاقَت قُلُوُب الْنَّاسٍ عَنْ مّافِي .. مِنْ خَيْرٍٍ فَعَفْوكَ لَا يَضِيْقْ ..

#3

السلام عليكم ,,

أهلاً بك أخ Robatic ,,

ماشاء الله شرحك جميل جداً و مبسط :clapping:

و أتمنى لو كنت أستطيع كتابة مثل هذه المقالات الرائعة :D

الحقيقة ليس لدي إلا تعليق بسيط جداً خارج نطاق المقال ,,

اقتباس
وهذا ما يسمى بالـ Generic Programming ونفس الأسلوب ينطبق على الـ Java & CSharp

الحقيقة أخي العزيز أن الـ Generic Programming هذه تتعلق بالـ Templates في ++C, أما الـ Polymorphism فهي تسمى عملية الربط المتأخر أو late binding للدوال,,

تحياتي لك ,,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 4 يناير 2008 في 02:18

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

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