دوره الدوت نت
الدرس(13): Oop
بالتفصيل...الجزء الثاني
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته
نبدا من حيث انتهينا من الدرس السابق
يجب قراءة الدرس السابق لتتمكن من الاستمرار
في هذا الدرس
وراثة الواجهاتInterface
Inheritance
تحدثنا سالفاً عن اشتقاق او وراثة فئة من
أخرى وفيها تورث الفئة المشتقة كافة خصائص ووسائل وأحداث الفئة الأب وكذلك أيضاً وراثة
الكود المكتوب بالداخل كاملاً. يُمكنك Visual Basic .NET
من تعريف أو التصريح عن الواجهات ، والواجهة ما هي إلا واجهة لفئة
ما واسمها خير دليل على هذا وهي لا تحتوي على أية أكواد للتنفيذ. بعد تصريحك عن واجهة
جديدة يمكنك استخدام الكلمة Implements<InterfaceName>
وذلك كي تشتق أو تستدعي كافة الوسائل والخصائص والأحداث التي تم التصريح عنها داخل
الواجهة.
كي تتضح لديك الرؤية أكثر من ذلك ، اتبع
ما يلي ... قم بالتصريح عن هذين المتغيرين كما هو موضح أدناه :
كود:
Dim CN1 As New
System.Data.OleDb.OleDbConnection
العجيب في هذين التصريحين أنك إذا نظرت
في الوسائل والخصائص المصاحبة للكائن CN1
ستجدها بنفس الترتيب ونفس الهجاء بالنسبة للكائن CN2 ، ولكن
مع اختلاف الوظيفة ... يعني مثلا الوظيفة Open
فهي مصاحبة لكلا الكائنين ولكنها مع CN1 تتصل بأي
قاعدة بيانات حسب جملة الاتصال الممررة إليها ConnectionString
، أما مع الكائن CN2
فهي لا تتعامل إلا مع محركات قواعد بيانات MSSQLServer فقط بالرغم
من أن اسم الوظيفة واحد في كلا الكائنين ... هنا يتضح معنى الواجهات.
سأوضح لك مرة أخرى وبمثال آخر .. بفرض أنك مدير مشروع
برمجي معين وليكن هذا المشروع خاص بصناعة مكتبة DLL
للتعامل مع ملفات MS OFFICE
، وقد قررت كمدير مشروع أن تقوم بعمل أربعة فئات 4Classes ، الأولى
منهم لـ MS Word والثانية لـ MS Access
والثالثة لـ MS PowerPoint
والرابعة لـ MS Excel
، وتم الاتفاق على عدة وظائف مثلا كالآتي :
Method >> OpenFile
Method >> DeleteFile
Property >> FileName
Method >> FileSize
وهذا فقط كان على سبيل المثال .. تعال معي
نرجع سوياً إلى موضوعنا الأساسي ، أنت كمدير مشروع كما ذكرنا سالفا أنك تريد تواجد
هذه الخصائص والوسائل في كل فئة من الأربعة فئات المرجو إنشائها. هل الحل أنك تقوم
بالنسخ واللصق ومن ثم تغيير الأكواد ، أم الإملاء على المطورين ومن ثم تجد الأخطاء
؟؟؟ ... هنا سنلجأ إلى استخدام الواجهات فهي من ستحل الموضوع.
من القائمة Project
اختر العنصر Add Component
ثم اختر العنصر Interface
كما هو موضح بالصورة أدناه :
قد قمت بالتصريح عن الوسائل والخصائص السالف
ذكرها في الواجهة كما يلي ... ولا زلت أكرر أننا لا نكتب أية أكواد داخل الواجهة ،
فقط نقوم بالتعريف والتصريح عن مكونات الفئة كما يلي :
Public Interface MyInterface
'This subroutine to open the file by using [FileName]
property value
Sub
OpenFile()
'Use this
property to specify file path or read the file path to open it or delete it
Property
FileName() As String
'This
subroutine to delete the file by using [FileName] property value
Sub
DeleteFile()
'This
function used to return the file size by using [FileName] property value
Function FileSize() As Double
End Interface
أنا فقط قمت بعمل مثال عابر ومبسط ، فيمكنك
عزيزي القارئ أن تقوم بالتصريح عما تحب وكيف تحب ومتي تحب ... قم بالتصريح مثلا عن
متغيرات عادية وأحداث ووسائل وخصائص ، أما أن لضيق الوقت أردت إيصال الفكرة فقط. أنا
متشوق بالفعل حتى أريك كيف يتم وراثة هذه الواجهة داخل الفئات التي قمنا بإنشائها سابقاً
، فكل ما عليك إلا الدخول داخل كل فئة وتكتب هذه العبارة :
كود:
Implements MyInterface
ستجد نفسك في الفئة مثلا الخاصة بالـ MS Excel قد حصلت
على النتيجة التالية كوراثة حقيقية لما تم التصريح عنه في الواجهة MyInterface:
Public Class Excel
Implements MyInterface
Public Sub
DeleteFile() Implements MyInterface.DeleteFile
End Sub
Public
Property FileName() As String Implements MyInterface.FileName
Get
End Get
Set(ByVal value As String)
End Set
End Property
Public
Function FileSize() As Double Implements MyInterface.FileSize
End Function
Public Sub
OpenFile() Implements MyInterface.OpenFile
End Sub
End Class
** ملحوظة **
كما ذكرنا سابقاً أن Visual
Basic .NET لا يسمح
لك بالوراثة من أكثر من فئة ، ولكن داخل الفئة الواحدة يمكنك وراثة أكثر من واجهة.
كلمة أخيرة في مبدأ الوراثة .. الكلمات
المفتاحية
Access Modifiers
Public:
يجعل المتغير أو الوسيلة معرفة على مستوى الفئة ككل وكذلك على مستوى المشروع بالكامل
، كما يمكنك الحصول على هذا المتغير أو هذه الوسيلة إذا قمت بأخذ نسخة Instance من الفئة
في أي مكان آخر على اختلاف نوع المشاريع سواء ******sApplications
أو ClassLibrary
... الخ. كما أن هذا المتغير أو الوسية من النوع Public
تظهر في حالات الوارثة في الفئات المشتقة.
Friend:
يجعل الإجراء معرف على مستوى الفئة ككل وداخل المشروع الحالي بصفة عامة ، لكن اذا تم
دمج الفئة في ملف DLL وأخذ نسخة لكائن من الفئة فإن الاجراءات من هذا النوع لا تظهر.
Default:
وهنا نحن بصدد الحديث عن عدم استخدام الكلمات المفتاحية والتي يعتبرها المترجم من أنها
من النوع Public وينطبق
عليها ما ورد سابقاً.
Private:
يجعل الإجراء معرف على مستوى على الفئة فقط ، ولا يظهر في أي مكان خارجها.
Protected:
يشبه الـ Private تماماً
ولكن يختلف عنه في نقطة ، ألا وهي أنه في حالة التوريث فإن الفئة المشتقة DerivedClass يظهر بداخله
الإجراءات والمتغيرات من النوع Protected
، ولكن إذا أخذت نسخة من الفئة الأب ****Class أو ما يسمى
بالـ ParentClass فلن تظهر المتغيرات ولا الإجراءات من النوع Protected.
التعدد [ Polymorphism
]
من أجمل ما قرأت عن التعدد أو الـ Polymorphism كان ذلك
في كتاب ( Visual Basic 2008
) لكاتبه Rod Stephens كان الآتي بالنص :
Roughly speaking, polymorphism means treating one object as another. In
OOP terms, it means that you can treat an object of one class as if it were
from a parent class.
فعلا هكذا ، أثناء تعرضك للتعدد أو الـ
Polymorphism فأنت يمكنك
معاملة الكائن كما لو كان كائن آخر .. على سبيل المثال فلنفترض افتراضاتنا السابقة
وهي أنه لدينا الفئة المسماة Employee
وكذلك الفئة Customer
وهما مشتقان من الفئة الأب المسماة Person.
من هنا يمكنك معاملة الكائنات المصاحبة للفئتين Employee
وكذلك Customer
كما لو انك تتعامل مع كائنات الفئة الأب Person وذلك لأنهم
بكل بساطة للفئة الأب وليس الابن كما يظهر لك. يعني هذا انك ظاهرياً تتعامل مع الفئات
Customer و Employee ولكن تستخدم
هاتين الفئتين كجسر من خلاله تتعامل في الباطن مع كائنات الفئة الأب Person.
يُمكنك الـ VisualBasic
.NET من تعيين أو تخصيص قيمة من فئة مشتقة إلى متغير في
الفئة الأب. في المثال التالي يمكنك أن تضع كائني Employee
أو Customer في متغير من الفئة الأب ... اتبع الكود ثم تابع رابط المشروع في الأسفل
:
كود:
Dim emp As New Employee 'Create an Employee.
Dim cst As New Customer 'Create a Customer.
Dim per As Person 'Declare a Person variable.
per = emp 'Ok .. An Employee is a Person.
per = cst 'Ok .. A Customer is a Person.
emp = per 'Not Ok .. A Person is not
necessarily an Employee.
** ملحوظة **
يمكنك الوصول للمعالم أو المكونات المُعرفة الخاصة
بنوع المتغير الذي تستخدمه للإشارة إلى كائن آخر. هذا يعني أنك مثلاً لو لديك متغير
من النوع Person يشير إلى
كائن من النوع Employee فإنك فقط تستطيع الاستفادة من معالم الفئة Person وليس لك
الحق في الاستفادة مما تم إضافته على الفئة Employee
بعد الوراثة.
اعادة التعريفOverloading
ماذا لو أنك قمت بتصميم فئة Class وأردت عمل
إجراء فرعي Subroutine بالاسم InsertPersonData
، وهذا الإجراء الفرعي يحتوي على الباراميترات أو المعاملات الآتية
:
FirstName
LastName
Age
Address
EMail
MobileNumber
المعاملان ( FirstName
, LastName ) يعدان من المعاملات الإجبارية
Not Optional Parameter ، أما عن بقية المعاملات فهي اختيارية حسب المتوفر لدى مستخدم الإجراء InsertPersonData ، ولو تخلينا
عن مبدأ الـ Overloading ضمن مبادئ الـ OOP فسيكون شكل الإجراء الفرعي كالآتي :
كود:
Public Sub
InsertPersonData(ByVal FirstName As String, ByVal LastName As String, Optional ByVal Age As Integer = 20,
Optional ByVal Address As String =
"", Optional ByVal EMail As String = "", Optional
ByVal MobileNumber As String = "")
'---------------------------
'Your Convenient Code Lines
'---------------------------
End Sub
فقد قمنا في الإجراء السابق بجعل بقية الاختيارات
من النوع Optional ولكن للأسف وبكل أسف ستجد أن المعاملات من النوع Optional يجب أن
تحتوي على قيم مبدئية وهذا نصاً للاعتراض الذي يصدره الـ Visual Basic .NET بالنص الآتي
:
Optional parameters must specify a default value
ولو طبقنا هذا الاعتراض في مثالنا الحالي
فسنجد مثلا أننا قمنا بتعريف المتغير Age
على أنه متغير رقمي من النوع Integer
ونوعه اختياري Optional
وقيمته الافتراضية = 20 ، وهذا يعني أن المستخدم لهذا الإجراء لن
يستطيع الاستفادة من هذا الإجراء لأنه لو لم يقوم بإدخال عمر الشخص فان البرنامج يقوم
بشكل افتراضي بوضع القيمة 20 في حقل العمر في حين أن المستخدم للإجراء لا يعرف أو لم
يتوفر لديه أية بيانات عن عمر الشخص.
أعتذر لو استطردت كثيراً في وصف المشكلة ، ولكن هنا
يأتي دور الـ Overloading ، فهل تعلم أن الـ Visual Basic .NET
يعطيك إمكانية تكرار أو إنشاء أكثر من وسيلة Method بنفس الاسم
ولكن بشرط اختلاف المعاملات. أي أنك من الممكن مثلاً أن تقوم بإنشاء الإجراء السابق
المسمى InsertPersonData أكثر من مرة وبنفس الاسم ولكن يختلف في المعاملات الممررة إليه. بالطبع
أثناء استخدامك للغات الـ .NET لاحظت الآتي :
إذا نظرت إلى ما يشير إليه السهم في الصورة
السابقة ستجد الرمز الآتي (1 of 12
) وهذا يعني بكل بساطة أن الإجراء أو الوسيلة المسماة Input
موجودة داخل الـ .NET Framework
اثنتي عشرة مرة ولكن مع اختلاف المدخلات أو المعاملات وهذا تطبيق
حي للـ Overloading.
تعال معي نضرب مثلاً آخر .. الفئة المسماة Person يظهر فيها
إجراءان كإجراءات مشيدة للفئة Constructor
Subroutines بالاسم
New. الإجراء المشيد الأول لا
يأخذ أية معاملات بينما الإجراء المشيد الثاني يأخذ المعاملان المتغيران ( FirstName , LastName ) كقيم افتراضية أثناء أخذ
نسخة Instance من الفئة.
وسيكون شكل الفئة Person بعد كتابة الكود كما يلي :
كود:
Public Class Person
Public
FirstName As String
Public
LastName As String
Public Sub
New()
FirstName
= "<fname>"
FirstName
= "<lname>"
End Sub
Public Sub
New(ByVal first_Name As String, ByVal last_Name As String)
FirstName
= first_Name
LastName
= last_Name
End Sub
End Class
هنا تضح فائدة الـ Overloading فبدلاً
من استخدام المعاملات الاختيارية Optional Parameters
وكذلك حالات الشرط IF Conditions
، فنلجأ حينئذ لمثل هذه الطرق والتي تحل مشاكل كثيرة. وفيما يلي مثال
صغير عن أخذ نسختين وإسنادهما للكائنات Person_1
وكذلك Person_2
كما يلي :
كود:
Dim Person_1 As New Person
Dim Person_2 As New Person("Ahmed",
"Negm")
ولكن هل جاء بذهنك أخي القارئ ما هي ميكانيكية
تحديد الإجراء ، يعني مثلاً أنه لدينا ثلاثة إجراءات بالاسم New
وبالطبع كما ذكرنا أن لكل منهما معاملاته الخاصة التي تختلف عن المعاملات
الخاصة بنظائره ، فكيف أقوم بتحديد التعامل مع الأول أو الثاني أو الثالث ... الخ ؟.
هنا يقوم الـ Visual Basic .NET بتحديد الإجراء حسب نوعية وعدد الإجراءات الممررة إليه وليس عليك أية التزامات
تجاه هذه الجزئية.
** ملحوظة مهمة جدا **
عند استخدامك للدوال أو ما يسمى بالـ Functions ، فيكون
نفس ما تمرسنا عليه في الإجراءات مع الـ Overloading
هو نفسه مع الـ Functions
، إلا أنه يوجد جزية هامة يجب التنويه عنها.
من البديهي أن يدور بذهن أحدكم فيما يخص
الـ Functions أنه إذا
تم تغيير نوع الدالة أو ما يسمى بالـ Return Type
فهذا يحقق الـ Overloading
... !!! ، وهنا سأصدمك بلا شك وأقول لك لن يحدث هذا ، فتحقيق الـ Overloading مع الدوال
لا يأتي بتغيير نوع الدالة فقط ، ولكن لابد تحقق شرط آخر وهو اختلاف المعاملات. انسخ
الكود التالي في محرر Visual Basic .NET
واقرأ نص الاعتراض بنفسك :
كود:
Function MySum(ByVal FirstNumber As Double, ByVal
SecondNumber As Double) As Double
Dim x As Double
x = FirstNumber + SecondNumber
Return x
End Function
'-------------------------------------------'
Function MySum(ByVal FirstNumber As Double, ByVal
SecondNumber As Double) As Integer
Dim x As Integer
x = FirstNumber + SecondNumber
Return x
End Function
وكي لا أرهقك في القراءة من المحرر فنص
الخطأ يفيد الآتي : " لا يمكن أن تتعدد الدالة cannot
overloaded each other وذلك لاختلافهم
فقط في النوع Return Type".
أما إذا أردت تصحيح الأوضاع فإليك الكود الصحيح :
كود:
Function MySum(ByVal FirstNumber As Double, ByVal
SecondNumber As Double) As Double
Dim x As Double
x = FirstNumber + SecondNumber
Return x
End Function
'-------------------------------------------'
Function MySum(ByVal FirstNumber As
Integer, ByVal SecondNumber As Integer) As Integer
Dim x As Integer
x = FirstNumber + SecondNumber
Return x
End Function
** تم إضافة الدالة المسماة MySum إلى المثال
OOP4 **
والان بحمد الله تم الانتهاء من الOOP بنوع من
التفصيل المجمل
ملاحظة:جميع الامثلة صممت ببرنامج ال VB2008.NET
ليست هناك تعليقات:
إرسال تعليق