دورة الدوت نت
الدرس (12) :oop
بالتفصيل ..الجزء الاول
OOP
_______________________________________
عندما نتحدث عن الـ OOP أو ما يسمى
بالـ [ Object-Oriented Programming
] ، فهنا نحن بصدد الحدي عن البرمجة الكائنية التوجهية او ما يسمى بالبرمجة الموجهة
للكائنات. يتناول هذا الموضوع باذن الله تعالى مميزات وسمات ومبادئ الـ OOP والكامنة
في الثلاث مبادئ الأساسية كما يلي :
الوراثة ... Inheritance
التغليف ... Encapsulation
التعدد أو تعدد الأوجه ... Polymorphism
هذا بالإضافة لعدة مميزات أو مبادئ أخرى
منبثقة من تلك الأساسية التي سبق وأن ذكرناها ، يمكنك متابعة ومعرفة الكثير عن الفئات
أو ما يسمى بالـ Class في الدرس رقم ( 11 ) مدخل الى الفئات..الجزء الثاني
ولكن دعونا نبدأ من هنا ، فأولاً كي نتعامل
مع الفئات فيجب أن يكون لكل فئة أحداثها وخصائصها وطرقها ( وظائفها ). فلو تناولنا
عناصر الفئة الثلاث المذكورين فيما سبق فسيكون الآتي :
الخاصية [ Property
] ... الخاصية هي إحدى الطرق التي عن طريقها يتم تخزين نوع أو شكل معين من أنواع البيانات
، ومن الممكن أن تكون هذه القيمة بسيطة كاسم أو رقم أو تاريخ ، ومن الممكن أن تكون
معقدة نسبياً كالمصفوفات أو تكون الخاصية أساسا عبارة عن كائن مستقل له خصائصه وأحداثه
وطرقه أو وظائفه. للعلم هناك خصائص من النوع ( Read/Write
) وهناك نوع آخر وهو ( Read-Only
).
الطريقة أو الوسيلة [ Method ] ... تمثل الطريقة دالة
أو إجراء ( Function Or Subroutine
). وتعتبر الطريقة أو الوظيفة كجزء من الكود داخل الفئة Class
مما يضفي عليها السمة الأساسية للفئات وكذلك تواجد الطرق داخل الفئات
لتنفيذ مجموعة من سطور الكود ضمنياً وتنفيذ وظيفة أو مهمة معينة ظاهرياً.
الحدث [ Event
] ... الحدث بكل بساطة هو عبارة عن إخطار حركي. يعني هذا أن الحدث يستدعي قطع أو إجراءات
معينة من الكود داخل الـ Class كي يخبرها أن بعض الأحداث أو الشروط داخل الفئة قد وقعت بالفعل.
اعلم بالطبع أني لم أضف لديك جديداً بل
بكل تأكيد قد شوشت عقلك وأذهبت لديك ببعض المفاهيم إلى حيث لا تدري ، ولكن أصغ لي مرة
أخيرة وتخيل معي هذا المثال البسيط.
بفرض مثلاً أنه لدينا فئة أو Class بالمسمى
( Job ) ، فهيا بنا نحلل ما جاء
بها كما يلي :
أولاً - الخواص [ Properties ] :
JobDescription
( وصف أو تعريف الوظيفة ) = وهي خاصية تحتوي على وصف نصي للوظيفة.
WorkHours
( ساعات العمل ) = وهي خاصية تحتوي على عدد ساعات العمل لتلك الوظيفة.
JobCustomer
( المتقدم للوظيفة ) = وهنا ستختلف نوع الخاصية فهنا سنقوم بتعريف الخاصية JobCustomer على أنها
عبارة عن كائن مشتق من فئة Class
آخر بالاسم Customer.
والفئة Customer تحتوي على
خصائص وأحداث وطرق ووظائف ومتغيرات وكذلك دوال وإجراءات.
EstimatedHours
( الساعات المُقدرة ) = عدد الساعات المبدئية المُقدرة لأداء هذه الوظيفة.
ثانياً – الطرق أو الوسائل [ Methods ] :
AssignJob
(اعتماد الوظيفة ) = هذا الإجراء يقوم بتنفيذ اعتماد الوظيفة من الموظف المختص بالتعيين
إلى العميل المتقدم للوظيفة.
BillJob
( كشف حساب ) = يقوم مثلا هذا الإجراء بطباعة كشف حساب على سبيل المثال للشخص المتقدم
للوظيفة.
EstimatedCost
( التكلفة المقدرة ) = دالة تقوم بالرجوع بالتكلفة المُقدرة بناءا على ما ورد في عقد
خدمات العميل أو المتقدم للوظيفة وكذلك الساعات المُقدرة التي وردت بالخصائص سابقاً
EstimatedHours.
ثالثاً – الأحداث [ Events ] :
Created
( إنشاء الوظيفة ) = يوفر هذا الحدث إمكانية التحكم بالفئة أو التطبيق عند إنشاء الوظيفة
عن طريق كتابة ما يشاء في هذا الحدث.
Assigned
( التعيين ) = يتم تنفيذ ما ورد في هذا الحدث عند موافقة الموظف على العميل المتقدم
للوظيفة وإعطاء أمر التعيين.
Rejected
( الرفض أو ترك المهمة ) = يتم تنفيذ هذا الحدث عند رفض إكمال المهمة من قِبَل الموظف
، ربما حدث هذا لعدم اكتمال أوراق العميل او ربما لظروف طارئة للموظف او عدم تواجد
الخبرة الكافية للقيام بهذه المهمة.
Canceled
( إلغاء الأمر ) = وهذا مثلاً يتم من قِبَل العميل قبل إصدار أمر التعيين.
كانت هذه نظرة سريعة على فئة أو Class تحتوي على
أحداث وخصائص ودوال وإجراءات وهذا فقط على سبيل المثال. نأتي بعد ذلك لملحوظة مهمة
وهي مجمل ما سبق فتعريف الفئة هي عبارة عن مصدر أو كيان أو شخصية برمجية مستقلة تغلف
او تحتوي على مجموعة من التعليمات البرمجية على اختلاف أنواعها ( خصائص أو أحداث أو
دوال وإجراءات ). لا يعني ما سبق هو شرط تواجد الثلاث مكونات السابقة داخل كل فئة ،
ولكن نحن نتحدث عن الأحوال القياسية.
بعد الحصول على نسخة من الفئة ووضع محتوياتها داخل
كيان آخر عن طريق جمل التصاريح مهما اختلفت صيغتها كمثل :
كود:
Dim MyObj As New Class1
فهنا يتضح الفرق بين الكائن والفئة على النحو التالي
، فهنا الفئة هي المسماة Class1 ، أما إن الكائن فهو المقصود به الكيان البرمجي الذي قمنا بوضع كافة خصائص
الفئة بداخله وهو المسمى في مثالنا MyObj.
___________________________
** ملحوظة مهمة **
___________________________
يعتبر الـ Visual
Basic .NET مكتظ بالفئات
، فعلى سبيل المثال تعتبر كل أداة موجودة داخل الـ Component
عبارة عن Class
مستقلة لها أحداثها وخصائصها وطرقها ، وتعتبر الفئة الأب هي الفئة
المسماة Control.
وعند استخدامك لهذه الأدوات فما أنت تعمل إلا مع كائنات مشتقة من مصدرها الأساسي ،
فلو فرضنا أننا لدينا TextBox على الـ Form فمربع النص هذا عبارة عن كائن مشتق من الفئة المسماة TextBox ويحمل هذا
الكائن كافة ما يميز هذه الفئة المشتق منها. باختصار وكما يقول البعض [ Visual Basic .NET is jam-packed with classes
].
___________________________
التغليف [ Encapsulation
]
الواجهة العامة للفئات Class's Interface تتكون مما
سبق ذكره وهي الخصائص وكذلك الطرق والأحداث ، وعندما أتحدث عن الواجهة العامة أقصد
بذلك الوظائف والأحداث والخصائص التي تظهر للمستخدم خارج الفئة.
كود:
Dim CN As New SqlClient.SqlConnection
CN.Open()
لو نظرنا في هذين السطرين السابقين فسيتضح
ما يلي ... أولاً الفئة المسماة SqlConnection
عبارة عن فئة مشتقة من مكتبة أو فئة كبرى مسماة SqlClient ، أما عن
الكائن CN فهو كائن
يحمل خصائص الفئة SqlConnection فإذا كتبت في محرر VB .NET
اسم الكائن CN ثم ضغطت علامة ( . ) ستظهر كافة الخصائص والطرق الخاصة بالفئة SqlConnection ولكن ستظهر
أسماء تلك الخصائص والطرق وليس الأكواد التي كُتبت بها تلك الطرق والخصائص.
تعال نضرب مثال آخر .. في مثالنا السابق
عن الفئة المسماة Job فهناك طريقة أو أجراء بالاسم AssignJob
ولكن من الطبيعي لتنفيذ هذه الوظيفة السابق ذكرها أن يكون هناك دالة
أثناء التنفيذ بأن يقوم البرنامج بفحص مؤهلات أحد الموظفين للقيام بمتابعة تعيين العميل
المتقدم للوظيفة كي ينفذ الوظيفة الرئيسية وهي التعيين ولتكن مثلاً اسم الدالة الفرعية
او الثانوية هذه بالاسم FindQualifiedEmployee.
وفيما سبق قصدت بأن الدالة المسماة FindQualifiedEmployee
غير ظاهرة لمستخدم الـ Class
لذا فهي تم تعريفها أو التصريح عنها بالمفتاح Private أما عن
الإجراء AssignJobفتم التصريح عنه بالمفتاح Public.
طبقاً لما سبق فإن مستخدم الفئةJobستظهر له الطريقة AssignJob
عند التصريح عن كائن كنسخة من الفئة ، أما الدالة FindQualifiedEmployee فهي خاصة
بالفئة فقط ولا تظهر للمستخدم للفئة.
بكل اختصار في حديثنا عن التغليف أو الـ Encapsulation ، أنت تتحكم
فيما يظهر لمستخدم الفئة وفيما لا يظهر من خصائص وطرق وأحداث. وما يظهر فهو يؤدي الوظيفة
التي وُجِدَت من أجلها الفئة ( Class
) ، أما ما لا يظهر للمستخدم من أحداث وخصائص وطرق فهو يخدم ما يظهر مما سبق.
يمكنك الاستفادة من مميزات التغليف Encapsulation بعيداً
عن الفئات. في العِقد السابق قبل اعتماد بعض اللغات لمبادئ الـ Object-Oriented كان المبرمجين
يقومون بصناعة مكتبات الكود أو تطبيقات معينة وتدعم أيضاً التغليف وبنفس المفهوم الحالي
ولكن كان آنذاك بمعنى آخر . على سبيل المثال : لو لدينا مكتبة تحتوي على وظائف ودوال
لعلم المثلثات Trigonometry مثلدوالالـ Sines أو Cosines أو Tangents أو Ar ctagents
...الخ ، ولتنفيذ هذه الحسابات فيجب أن تحتوي المكتبة على وظائف ودوال مساعدة خفيّة
تساهم في الوصول لنتائج الدوال الأساسية السابق ذكرها.
الوراثة [ Inheritance
]
الوراثة ضمن مبادئ الـ OOP عبارة عن
اشتقاق فئة من فئة أخرى. وتكون الفئة الجديدة المشتقة ( ChildClass or DerivedClass or SubClassing
) تحتوي على كافة خصائص وأحداث وطرق الفئة الأب ParentClass.
هذا بالإضافة إلى انك تستطيع إضافة أحداث وخصائص ودوال وإجراءات جديدة إضافة لما حصلت
عليه عن طريق الوراثة من الفئة الأب من الخصائص والأحداث والدوال والإجراءات.
على سبيل المثال : لدينا فئة بالاسم Person ولهذه الفئة
بعض المتغيرات كـ ( FirstName , LastName , Age ,
Address , EMail ) ، ومثلاً هناك وظيفة اسمها
DialPhone وهذه تختص
بقيام التطبيق بالاتصال تليفونياً بالشخص وكذلك وظيفة أخرى اسمها SendMail وهذه تُمكن
المستخدم من إرسال بريد إلكتروني للشخص.
إذا أردت إنشاء فئة جديدة بالاسم Employee فهنا ستجد
أن كل موظف ما هو إلا شخص عادي لديه الخصائص والوظائف السابق ذكرها ( FirstName , LastName , Age , Address , EMail
) وكذلك DialPhone و SendMail.
وهنا إذا لم تكن تعرف للوراثة معنى فستقوم بإنشاء تلك الخصائص أو المتغيرات والوظائف
ثانية من جديد ، وستضيف عليها طبعا بعض الخصائص التي تخص الموظف مثل ( Salary , WorkHours , WorkAddress , WorkPhone , EmployeeID ,
OfficeNumber ) ، وتريد كذلك استخدام الوظيفة
DialPhone ولكن بشكل
آخر وبأسلوب أخر.
وكذلك ستظل المشكلة قائمة إذا أردت عمل
فئة جديدة بالاسم Manager ، فستحتاج كافة الخصائص والوظائف الموجودة في كلتا الفئتين Person وكذلك Employee وستضيف
عليها لاحقاً بعض الخصائص والأحداث والوظائف وذلك لان كل مدير ما هو إلا موظف وما كل
موظف إلا شخص عادي.
الشكل السابق كان مثالاً لمشكلتنا السابق
تفصيلها. من مميزات الوراثة والتوريث الأساسية هي إعادة استخدام الكود مرة أخرى دون
إنشاؤه أو حتى إعادة كتابته مرة أخرى. ومعنى هذا أنك مثلا عندما ستكون بصدد إنشاء الفئة
المسماة Employee ستقوم بإنشاء
( FirstName , LastName , Age , Address , EMail
) وكذلك الوظائف والطرق السابق ذكرها. ولكن بمجرد أخذك قرار الوراثة من الفئة الأب
Person ستُحَل
هذه المشكلة بالكامل.
التوريث المتدرج Inheritance
Hierarchies
إعادة استخدام الكود في التوريث لا يحميك
من الخطأ في إعادة كتابة الكود فحسب ولكن كذلك يجعل تطوير الفئة أو التطبيق بصفة عامة
اسهل من ذي قبل. بفرض مثلاً أنك تقوم بتطوير مجموعة من الفئات كما في الشكل السابق
، وبعد انتهائك من الفئات Person كفئة أساسية ParentClass
، وكذلك الفئات المشتقة مثل Employee
و Manager و Customer و Secretary كفئات مشتقة Subclasses
... أردت أن تضيف الخاصية BirthDate ، فلو ابتعدت عن مبادئ التوريث التي جاءت بها البرمجة الموجهة للكائنات
فستقوم بإضافة هذه الخاصية إلى كل الفئات السابق ذكرها ، أما مع OOP ومع Inheritance فما عليك
إلا أن تضيف هذه الخاصية في الفئة Person
لأنها الفئة الأب ومن ثم للفئات الأخرى يورثوا خواص الفئة الأب وانتهى
الموضوع وتم حل المشكلة.
كمثال آخر ، بفرض أنك تريد تعديل أو حذف
أحد الخصائص أو الوسائل ولتكن مثلاً داخل خاصية FirstName
، فما عليك إلا أن تقوم بتعديل الخاصية المذكورة في الفئة الأب وهي
Person ولا عليك
أن تقوم بالتعديل في كل الفئات التي تحمل نفس الخاصية.
** معلومة **
تدعم بعض اللغات التوريث المتعدد ، بمعنى
أنه يمكنك في فئة مشتقة من أكثر من فئة أب. فلنفترض أن هناك فئة بالاسم Car وتحتوي
على بعض الخصائص والوسائل والأحداث الخاصة بهذه الفئة ، وهناك فئة أخرى بالاسم House ولهذه الفئة
أيضا ما يميزها من الخصائص والأحداث. ولو استخدمنا التوريث المتعدد MultipleInheritance فسنحصل
على فئة أخرى ولتكن بالاسم MotorHome
ويحمل مميزات كلتا الفئتين Car
و House.
أنا شخصياً – بعقلي الضيق الفكر - غير متخيل الفئة
الناتجة لأن الفئات الأساسية غير متجانسة ، ولكن Microsoft
أشفقت عليّ وجعلت VisualBasic.NET
لا يدعم التوريث المتعدد وجعلت الفئة لا تورث من أخرى إلا مرة واحدة
فقط. إذا احتجت لاستخدام التوريث المتعدد فعليك أن تستخدم الواجهات Interfaces ، فبدلا
من تعريف أكثر من ParentClass فقم بتعريف أو التصريح عن أكثر من ParentInterface
وعندها يمكنك إنشاء فئة مشتقة تنفذ أو تطبق داخلها أكثر من واجهة
Interface كما يحلو
لك. وللعلم لا تورث الفئة أية أكواد من الواجهات ولكن من اسمها ستأخذ فقط واجهة وتضيفها
على الفئة.
التوريث وإعادة القيادة Overriding
إذا قمنا باشتقاق الفئة Employee من الفئة
Person ، فسنقوم
بإضافة خواص ووسائل جديدة للفئة Employee
كما تم الذكر في أول الموضوع ، ولكن من ضمن المشاكل التي واجهتنا
هي أن الوظيفة DialPhone تقوم بالاتصال بالشخص عن طريق المودم ولكن تتصل بتليفون المنزل ، أما في
الفئة Employee نحتاج نفس الوظيفة وبنفس إمكانياتها باستثناء رقم التليفون ، فنحن نريد
استبداله بتليفون العمل. لو بعدنا قليلاً عن OOP
وكذلك الـ Overriding
فسنقول أن الحل هي إنشاء الوظيفة من جديد ، ولكن مع OOP فسنستخدم
إحدى مميزاتها الفرعية وهي إعادة القيادة للوظائف والوسائل ، ولكن من شروط استخدام
إعادة القيادة هي أن يكون الإجراء الأساسي في الفئة الأب معرف أو مصرح على أنه قابل
لإعادة القيادة من مكان آخر. فيجب أن يكون الإجراء في الفئة الأب معرف كما يلي :
كود:
'Dial the phone using (Phone)
property.
Public Overridable Sub DialPhone()
MsgBox("Dial : " & Me.Phone)
'Other
Code
End Sub
وعند الإشتقاق أو التوريث وإقرار تولي القيادة
من الفئة المشتقة فسيكون الإجراء معرف أو مصرح عنه كما يلي :
كود:
'Dial the phone using
(WorkPhone) property.
Public Overrides Sub DialPhone()
MsgBox("Dial : " & Me.WorkPhone(
'Other
Code
End Sub
تتيح لك إعادة القيادة استخدام إجراء بنفس
الاسم مرة أخرى ولكن تحت أكواد جديدة ووظائف من الممكن أن تكون مغايرة تماما للوظيفة
الموجودة في الفئة الأب ولكن بشرط أن تكون الوظيفة معرفة في الفئة الأب على النحو التالي
Public Overridable Sub ويقصد بها أي ان هذا الاجراء قابل لإعادة تولي القيادة من مكان آخر ، ولتولي
القيادة من مكان آخر فيجب التصريح عن الوظيفة في الفئة المشتقة كما يلي Public Overrides Sub ، وبهذا
تكون قد أخفيت هذه الوظيفة في الفئة الأب وأظهرتها من جديد في الفئة المشتقة وبشكل
جديد وذلك كما رأينا في مثالنا السابق.
** ملحوظة **
كي تعطي أمر بالوراثة من فئة أب فما عليك
إلا كتابة ما يلي :
كود:
Inherits Person
حيث Person
هي الفئة الأب المشتق منها خصائصها داخل الفئة المشتقة ولتكن كما
ذكرنا الفئة Employee
... يمكنك النظر في المشروع التالي لرؤية الأكواد المتعلقة بالوراثة وكذلك إعادة القيادة
، ولكن عند تحميلك للمثال ستجد به فئة بالاسم Person
وفئة أخرى بالاسم Employee
ونموذج بالاسم Form1
، قم بتعريف كائنات جديدة كنسخة من الفئات السابق ذكرها وشاهد مميزات
إعادة القيادة وكذلك مميزات الوراثة بنفسك.
بما أننا قد ذكرنا إعادة القيادة Overriding فتعال نذكر
شكل آخر من إعادة القيادة وهو Shadow.
إعادة القيادة والظل Shadows
تحت العنوان الجانبي السابق (التوريث وإعادة
القيادةOverriding
) قمنا بطرح مشكلة وهي إعادة كتابة إجراء بنفس الاسم لكن مع اختلاف الوظيفة التي خُلِق
من أجلها. هنا تظهر مشكلة جديدة : ماذا لو أننا لدينا الفئة Person
بما فيها للإجراء DialPhone
ولكنه غير قابل لإعادة القيادة من مكان آخر ، وكذلك الفئة Person تم تغليفها
وجلبها للمشروع في صورة DLL File
وعندئذ لا يمكن التعديل فيما ورد بها وكذلك الإجراء المسمى DialPhone في الفئة
الأب غير قابل لإعادة تولي القيادة يعني أنه لم يصرح عنه كـ Overridable Sub وبالتالي
لا يمكننا إعادة تولي قيادته طبقاً لم تم شرحه في العنوان الفرعي السابق ( التوريث
وإعادة القيادة ) ، ولكننا في نفس الوقت نريد إعادة تولي القيادة للإجراء DialPhone وتغيير
بعض خصائصه وعدم إظهار نفس الإجراء في الفئة الأب ... ماذا سنفعل ؟؟؟. باستخدام للكلمة
المفتاحية Shadows أثناء تصريحك عن الإجراء في الفئة المشتقة بهذا تكون قد فعلت أو قمت بما
تقوم به Overridable Sub دون أن يكون الإجراء قابل لإعادة تولي القيادة من مكان آخر ، فكلمة Shadows تخفي الإجراء
في الفئة الأب وتُظهر فقط الإجراء بنفس الاسم في الفئة المشتقة.
** ملحوظة مهمة **
بفرض أن هناك في الفئة الأب متغير نصي بالاسم
( FirstName ) ، وفي الفئة المشتقة يوجد
إجراء فرعي بالاسم ( FirstName
) .. هنا لا تستخدم shadows لان هذا سيسبب تعارض لان أنواع الكائنات غير متجانسة فهذا متغير وهذه Method.
مع تحياتي: عبدالقادر البعداني
--------
هنا ينتهي الجزء الاول من الدرس
--------

ليست هناك تعليقات:
إرسال تعليق