البرمجة غرضية التوجة في دلفي

الجزء الأول :

غرضية التوجة في دلفي object-oriented in Delphi :

إن معظم اللغات الحديثة تدعم البرمجة غرضية التوجة (OOP) أو object-oriented programming , حتى أن مقدار دعم اللغة للـ OOP أصبح ينظر إلية في كثير من الأحيان كمقياس يعبر عن أهمية اللغة .
تعتمد البرمجة غرضية التوجة على ثلاث مفاهيم أساسية سنعالجها في هذا الفصل :

- التغليف encapsulation .
- الوراثة inheritance .
- تعددية الأشكال polymorphism .

--------------------------------------

دلفي هي توسيع غرضي_التوجة للغة باسكال التقليدية .وما أريد التنوية إلية هنا أن الصياغة النحوية للغة الباسكال مشهورة بإنها صياغة أكثر وضوحا وقابلية للقراءة من معظم اللغات الأخرى (ولنقل مثلا لغة C ) , وبالتالي كمية أكبر من الحشو من أجل الحصول على شفرة مقروءة تشبة الكلام العادي بحيث يمكن فهمها وتذكرها بشكل سريع ومنتظم وهذا مما يقل من الوقوع بالأخطاء .
كما أن التوسع الغرضي_التوحه لهذة الغة بإعتراف الجميع لا يقل أهمية عن الموجود في النسل الحالي للغات البرمجة الغرضية من Java حتى C# .

الأصناف والأغراض :

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

ومن المهم أن تفهم أن كل مكون تم وضعة على الشكل هو عبارة غرض , وهذا الغرض خاص بصنف معين ...

توضيح :
المصطلحان غرض وصنف يستخدمان بكثرة , وكثيرا ما يتم إساءة فهمهما , أكاديميا نستطيع القول أن :
الصنف : هو نمط معطيات معرف يملك بعض التوصيفات , وبعض العمليات " التصرفات" (أو ما يسمى مناهج) .
الغرض : هو منتسخ من الصنف , (أو متغير من نمط معطيات صنف ) . بحيث يملك قيم لكل التوصيفات التي يحددها الصنف , ويمكنة إستعمال عملياته ويخضع لتصرفاتة وخواصة ,..

دعنا نضرب مثلا من الطبيعة على هذا الموضوع :
في حالة الإنسان مثلا , إننا عندما نقول إنسان ((بشكل عام )) فنحن ندل على الصنف إنسان . أي الإنسان هو الصنف , وأصبحنا نعرف بعض التوصيفات (الخصائص) المتعلقة بة , مثلا أن له إسم يميزة و طول ووزن ولون عيون وأنة يقوم ببعض السلوكيات (المناهج) مثل الأكل والشرب والركض والكلام , تذكر أنة عندما نتحدث عن الصنف إنسان فنحن لانحدد أي قيمة ثابتة لأي من خصائصة مثلا لايجوز القول أن طول الإنسان هو 175.3 متر دوما أو حتما ....
إذا كان الإنسان هو الصنف فما هو الغرض إذا , الغرض يجب أن يكون حالة من حالات الصنف , حالة واحدة محددة جيدا وتعطي الأجوبة والقيم الدقيقة لكل الصفات المتعلق بالصنف التابعة له ,
الغرض في هذة الحالة هو إنسان ما , أي إنسان محدد , ولنقل عروة عيسى ,
الغرض عروة من الصنف إنسان يملك جميع خواص الإنسان ويأخذ قيم محددة لها , الإسم عروة , الطول 182.3 متر , الوزن 85كغ , لون العينين بني ... الخ ... ويستطيع تنفيذ كل من مناهجة (سلوكياتة) متى شاء من الركض والأكل والكلام الخ ..
وبالتالي للصنف إنسان عدد كبير من الأغراض مثل عروة وكمال وعلاء وعصام وعلي الخ.... وكل منهم يملك صفات الصنف الأساسية من أجل قيم محددة خاصة به هو ....

أتمنى أن يكون الأمر أصبح واضحا الآن .

- كثيراً ما يشار إلى الأغراض بالكيانات (entities) .
العلاقة بين الغرض (object) و الصنف(class) هي نفسها العلاقة بين المتحول(variable) والنمط (type) .
مثلا هي العلاقة بين Integer والمتحول A حيث A : integer ;

دعنا نوضح بعض المسائل المهمة هنا :
إن متحول من نمط صنف معين في دلفي كما هو الحال في معظم لغات البرمجة الغرضية التوجة لن يمثل تخزينا للغرض في الذاكرة ضمن هذا المتحول , ولكنة فقط عبارة عن مؤشر إلى الغرض في الذاكرة .
وبناء على ذلك فإننا لا نستطيع التعامل مباشرة مع الأغراض كما نتعامل مع المتحولات العادية , لإن الأغراض لن تخزن في ذاكرة المتحول المعرف لها كما يحدث في حالة متحول عادي , بل هي بحاجة إلى حجز يدوي لمساحة ذاكرة خاصة بها ومن ثم يصبح المتحول الخاص بها يؤشر على هذة المساحة , أي يخزن عنوان هذة المساحة فقط , ولايخزن بيانات الغرض ضمنة..
إذن وجدنا الآن فرقا مهما بين طريقة التعامل مع المتحولات العادية ومع الأغراض , وقلنا أنة يجب حجز (إنشاء) ذاكرة للغرض قبل إستخدامة ,,, كيف يتم ذلك ؟
يتم ذلك بإحدى طريقتين :
1- إنشاء وحجز الذاكرة يدويا لغرض من صنف ما .. ( بإستخدام المنهج الباني Create)
2- أو نسب الغرض إلى غرض موجود تم حجز الذاكرة له مسبقا .. (بإستخدام النسب := )

var
Obj1, Obj2: TClass;
begin
// assign a newly created object
Obj1 := TMyClass.Create;
// assign to an existing object
Obj2 := Obj1;

في هذا المثال قمنا بتعريف غرضين Obj1, Obj2 من الصنف Tclass , الغرض الأول obj1 قمنا بإنشاءة بالطريقة الأولى بإستخدام المنهج المهم والشائع Create الذي يحجز الذاكرة لة ويهيئة للإستخدام .والغرض الثاني obj2 قمنا بنسبة إلى غرض موجود مسبقا وهو Obj1 , وكلا الطريقتين صحيحتين ومستخدمتين .

مهم :
بما أننا قمنا بحجز الذاكرة يدويا بإستخدام المنهج Create , فإنة يتوجب علينا تحريرها يدويا أيضاً , ويتم ذلك بإستخدام المنهج Free . أنصحك بإستخدام معالجة الإستثناءات Try) .... Finaly) لتعريف الأصناف وتحريرها ,




يتبع ..
عروة عيسى 12/8/2004
المنتديات

التعليقات

orwa

تعريف الأصناف في دلفي : لتعريف صنف جديد في دلفي , دعنا نتذكر أن الصنف يحوي شيئين مهمين هما الحقول (بيانات الصنف) والمناهج (عمليات الصنف) . بناء صنف جديد موضوع سهل في دلفي . إذا فكرنا بناءً على ما سبق ماذا نحتاج لنعرف صنف جديد , لوجدنا أننا نريد تعريف الحقول التي يحويها هذا الصنف والعمليات التي يستطيع إنجازها , وبالتأكيد نعرف له إسما فريدا خاص به . الحقول هي عبارة عن متحولات عادية , والعمليات هي عبارة عن مناهج (أي توابع أو إجراءات) . تنسيق هذا التعريف يتم بصورة بسيطة – بإن نذكر في قسم Type إسم الصنف محدداً بالكلمة المفتاحية Class ونتبعه مباشرة بتعريف الحقول الخاصة به , ثم رؤوس المناهج التي يعرفها , وبالتأكيد ننهي ذلك بـ End; : Type TDate = class Month, Day, Year: Integer; procedure SetValue (m, d, y: Integer); function LeapYear: Boolean; // السنة كبيسة يحدد إذا كانت end; ملاحظة : إن البادئة T التي سبقنا بها إسم المتحول هي عبارة عن تقليد (عرف) للمترجم , يتبعة مبرمجو الدلفي منذ ظهورها بإيعاذ من شركة بورلاند نفسها . الحرف T هو إختصار لـType , وهو مجرد حرف ولكن إتباع هذا العرف سيجعل شفرتك مفهومة أكثر من قبل بقية المطورين الذين اعتادو على ذلك . ربما لاحظت من تعريف الصنف أننا قمنا بتعريف أسماء التوابع والإجراءات فقط (رؤوس المناهج) ولم نقم بكتابة أجسامها هناك , حيث نقوم بتعريف أجسام المناهج (توابع+إجراءات) في جسم الوحدة نفسها , أي قسم الـ Implementation الخاص بها . وبما أننا نستطيع تعريف أكثر من صنف في الوحدة (Unit) ولكل صنف مناهج خاصة به لذلك يجب تمييز جسم كل منهج لنعرف لإي صنف يتبع . من أجل ذلك فإن تعريف أجسام المناهج يسبق بإسم الصنف مفصولا بنقطة عن إسم المنهج مثلا TDate.SetValue : Implementation … procedure TDate.SetValue (m, d, y: Integer); begin Month := m; Day := d; Year := y; end; function TDate.LeapYear: Boolean; begin // call IsLeapYear in SysUtils.pas Result := IsLeapYear (Year); end; فكرة : إذا ضغطت Ctrl+Shift+C عندما يكون المؤشر ضمن تعريف الصنف , فإن ميزة تكميل التعريف في دلفي سوف تقوم تلقائيا بمساعدتك وتوليد هيكل التعريف الخاص بالمناهج التي قمت بتعريفها في الصنف . عرفنا الآن كيف نبني صنف جديد , وأننا نستطيع أن ننشيء أغراضاً من هذا الصنف وإستخدامها في شفرتنا . مثال على ذلك : var ADay: TDate; begin // create an object ADay := TDate.Create; try // use the object ADay.SetValue (1, 1, 2000); if ADay.LeapYear then ShowMessage ('Leap year: ' + IntToStr (ADay.Year)); finally // destroy the object ADay.Free; end; end; - لاحظ أن التعبير ADay.LeapYear مشابة تماما للتعبير ADay.Year . مع أن إحداهما هو تابع للتنفيذ , والآخر هو متحول (وصول بيانات مباشر) , أي نصل لبيانات الغرض بنفس طريقة الوصول لمناهجة وذلك بإستخدام النقطة . -بإمكانك إختيارياً أن تضيف قوسان مغلقان بعد إستدعاء تابع ليس لة بارامترات , وبإمكانك تجاهلهما على كل حال مثال : التعريفان التاليان متكافئان : GetCurrentDay(); GetCurrentDay; التحميل الزائد للمناهج method overloading : دلفي تدعم التحميل الزائد للمناهج (التوابع + الإجرائيات) . ولكن ما هو التحميل الزائد ياترى ؟ التحميل الزائد يعني أن يكون لديك منهجان بنفس الإسم , شريطة أن تقوم بإضافة الكلمة المفتاحية overloading اليهما وأن تكون قائمة البارامترات (المتغيرات) الخاصة يكل منهما مختلفة عن الآخر , (إذا إتفقا بالإسم فإن دلفي بحاجة إلى شيء آخر للتمييز بينهما لذلك فإن منهجين متفقين بالإسم والبارامترات لا يمكن أن نحديد أي منهما نريد أن نستخدم ) . بفحص البارامترات يستطيع مترجم اللغة (Compiler) تحديد أي واحد منهما نريد أن نستدعي , ويقوم بالإستدعاء الصحيح للمنهج الصحيح . سنرى تطبيقات هذة الميزة لاحقا يتبع ...
orwa

نظرة أكثر تفصيلا في غرضية التوجة : - التغليف - الوراثة - تعددية الأشكال التغليف Encapsulation : تعتمد فكرة غرضية التوجة على إخفاء البيانات . وتستخدم الأصناف لتحقيق ذلك . يتم إخفاء البيانات داخل الأصناف الخاصة بها , أو نقول يتم تغليف البيانات داخل الأصناف . عادة يتم توضيح هذة الفكرة بإستخدام ما يسمى الصناديق السوداء (black boxes) , حيث لا تضطر أن تعرف كيف تتم الأمور بالداخل وما هي المحتويات الداخلية , وكل ما يهمك هو كيف تتعامل مع واجهة الصندوق الأسود وتعطية معطياتك وتأخذ النتائج بغض النظر عن ما يتم في الداخل . إن ما يهمك فعليا من الصندوق هو آلية التعامل معة (مع واجهتة) ولا تعطي إهتماما كبيرا عن تفاصيل داخل الصندوق , مثلا يهمك أن تتفرج على البرامج المفضله على التلفزيون وأن تعرف تغيير المحطات وإطفاءة وتشغيلة , بغض النظر عن فهم الدارات الداخلية المكونة للتلفزيون .. إذن نخزن البيانات داخل الأصناف وعندها يمكننا أن نكتفي بمعرفة كيفية إستخدامها من الخارج . إن كيفية الإستخدام تدعى واجهة الصنف (class interface) وهي التي تسمح للأجزاء الأخرى من البرنامج بإستخدام الأغراض المعرفة من هذا الصنف , وبالتالي عندما تستخدم غرض ما فإن معظم شفرته تكون مخفية , ونادرا ما تعرف ما هي البيانات الداخليه له حتى أنه قد لا توجد طريقة لدخول البيانات الخاصة به بشكل مباشر مالم تستخدم المناهج المتاحة على الواجهة والتي تسمح لك بتغيير وقراءة البيانات , وذلك يعتبر من أهم الفروق بين البرمجة غرضية التوجة و البرمجة الكلاسيكية والتي تكون البيانات فيها عامة لكل الأصناف غير تابعة لصنف محدد كما أنك تستطيع تغيرها مباشرة وبالتالي تقع في مطب عدم صلاحية القيمة لحالة أو لمجموعة حالات ,,... لنوضح هذة النقطة الهامة : في مثال التاريخ السابق , - لو كنا نتبع الطريقة الكلاسيكية بالبرمجة فإن المتحولات (البيانات) ستكون متاحة للدخول والتغيير المباشر , وبالتالي من الممكن إدخال قيم غير صالحة بدون وجود إمكانية للتأكد منها , مثلا لو قمت بإدخال التاريخ February 30 (30 شباط) والذي هو تاريخ خاطيء لإن شباط لايحوي 30 يوم فإن البرنامج سيقبلة لإننا عرفنا متحول اليوم من النوع الصحيح (Integer) الذي يقبل هذة القيمة , وستحصل الأخطاء لاحقا عند العمليات الحسابية , أو تعطي نتائج خاطئة تماما . - أما في حال البرمجة الغرضية , فإن الدخول المباشر للبيانات غير مسموح لإن البيانات مغلفة (مخبأة ) في الصنف والوصول إليها يتم بإستخدام المناهج التي خصصها الصنف لذلك , أي أنك لن تستخدم المتحول مباشرة بل ستتعامل مع إجرائية أو تابع لإدخال القيمة , وبالتالي لن يظهر معنا النوع السابق من الأخطاء لإن هذة المناهج يمكن بسهولة تضمينها شفرات لفحص القيم والتأكد منها , ورفض التعديلات في حال كانت القيمة غير صالحة . لاحظ أننا أستخدمنا المنهج SetValue لضبط القيم ولم نقم بالضبط المباشر وهنا نستطيع إضافة الشفرة الخاصة بالتحقق من صلاحية القيمة , كأن تكون أصغر من حد معين , أو غير سالبة , أو أو ... • كما أن للتغليف ميزة سحرية للمبرمج نفسه هذة المرة .. لإنها تسمح لة بتغيير التركيب الداخلي للصنف في التحديثات المستقبلية , وبالتالي ستطبق التغييرات تلقائيا على بقية الأغراض التي إستخدمت هذا الصنف بإقل عناء ممكن , دون الحاجة لتغير شفرتنا في مناطق مختلفة من البرنامج . ملاحظة : بالإضافة إلى التغليف المعتمد-على–الصنف فإن دلفي تدعم التغليف المعتمد-على-الوحدة , بحيث كل متغير تقوم بتعريفة في قسم الـ Interface للوحدة سيصبح مرئيا لباقي وحدات البرنامج عند إستخدامها في التعريف Uses في حين أن المتغيرات المعرفة في قسم الـ Implementation هي متغيرات محلية لهذة الوحدة فقط . محددات الوصول Private, Protected, Public : دلفي تملك ثلاث محددات وصول من أجل التغليف المعتمد-على-الصنف . وهي Private, Protected, Public , تتحكم محددات الوصول بمجال الرؤيه المسموح به من أجل حقل أو منهج ما • التوجيه Private يدل على حقول الصنف ومناهجه التي تكون غير متاحة خارج الوحدة التي عرف فيها الصنف , ولا يمكن الوصول إليها سوى من داخل هذه الوحدة , وبالتالي هي متغيرات محليه في هذة الوحدة تستخدم لإتمام عمل جزئي ما داخل الصنف ولا داعي لظهورها لبقية العناصر . • التوجيه Protected يستخدم لتحديد مجال رؤيه مقيَّد لحقول الصنف ومناهجه , حيث يستطيع الصنف الحالي والأصناف المورثة منة فقط الوصول إلى البيانات المعرفة ضمنه , وببساطة يستطيع الصنف الأساسي والأصناف المشتقة منه بالإضافة إلى أي شفرة في نفس الوحدة الدخول إلى البيانات المحمية بـ Protected وفقط هؤلاء هم من يملكون سماحية الدخول . هذا التوجيه مثل سابقة من ناحية أنه محلي ضمن الوحدة , ولكن نضيف هنا إمكانية الرؤية من قبل الأصناف الجزئيه المشتقة التي ربما تحتاج هذة البيانات . • التوجيه Public يدل على حقول ومناهج يمكن الدخول إليها بحريّة من أي جزء من البرنامج كما لو أنها معرفة بنفس الوحدة , حيث لا توجد قيود في الدخول إلى البيانات المعرفة بهذا التوجية . تحذير : محددات الوصول السابقة تقوم بتحديد إمكانية دخول شفرات من خارج الوحدة إلى الصنف المعرف فيها , وبالتالي إذا وجد صنفان في نفس الوحدة فلا توجد حماية لدخول إحدهما إلى حقول المعرفة Private من الصنف الآخر .. التغليف بإستخدام الخصائص Properties : الخصائص تعتير من أروع تقنيات البرمجة الغرضية , وتمثل فكرة التغليف بشكلها الأمثل . والخصائص بشكل عام هي أول ما تعلمنا التعامل معه في مرحلة المبتدء , وللتبسيط فإن كل ما تراه في ضابط الكائنات عبارة عن خصائص , والفكرة هي أنك تتعامل مع إسم , والذي يخفي عنك بشكل كامل تفاصيل التنفيذ , وتصبح مهمتك الحالية كمستخدم للصنف هي قراءة القيم منة أوكتابتها إلية , أعجبني تعريف أحد الكتاب عندما قال أن الخصائص هي حقول إفتراضيه (virtual fields) . ربما لاحظت في الفقرة السابقة أننا يجب أن ندخل للبيانات في حالة البرمجة الغرضية عن طريق المناهج بدلا من الدخول المباشر , وهذا يبدو شيئا مربكا قليلا , خاصة أن منهج القراءة سيكون مختلف عن منهج الكتابة , إذا بنينا حقول إفتراضية تستخدم هذة المناهج وتخفيها عنا فإننا سنكسب سهولة الدخول المباشر للبيانات وقوة التغليف . هذة الحقول الإفتراضية هي الخصائص , ونتعامل معها مثلما نتعامل مع الحقول العادية . تأمل الشفرة البسيطة التالية : Edit1.Text := Button1.Caption; لاحظ أنان إستخدمنا الخاصية Text المتعلقة بالغرض Edit1 للكتابة فيها , والخاصية Caption من الغرض Button1 للقراءة منها . وببساطة أصبحنا بهذة الفكرة الرائعة نضيع الوقت بالتفكير بدلا من إضاعة الوقت بكتابة الشفرة , بالتأكيد توجد مناهج خاصة للكتابة إلى الخاصية Text وللقراءة منها , لكن الخاصية Text أخفت هذة الإرباكات عنّا وسمحت لنا بإستخدامها بغض النظر عن معرفتنا بشفرتها المخفية محققة بذلك تغليفا مثالياً . تعريف خاصية جديدة : الفقره السبقة تكلمت عن مستخدم الصنف الذي يستطيع إستخدام الخواص بسهولة , هذة الفقرة ستتكلم عن باني الصنف الذي يؤمن هذة السهولة . عرفنا الآن أن للخاصية إزدواجية بالتعامل .. مرة قراءة , ومرة كتابة وبناء على ذلك لتعريف خاصية ما نحن نحتاج لتعريف قابلية القراءة وقابلية الكتابة أيضا , ويتم ذلك ببساطة عن طريق الكلمتين المفتاحيتين Read , Write كما أننا نستخدم الكلمة المحجوزة property لتعريف خاصية جديدة أمثلة: 1- property Month: Integer read FMonth write FMonth; 2- property Month: Integer read FMonth write SetMonth; 3- property Month: Integer read GetMonth write SetMonth; حيث Fmonth متغير معرف كـ Private , و SetMonth إجرائية و GetMonth تابع معرفان ضمن الصنف . الحالة الأولى : وهي أبسط الحالات , أن نقوم بتعريف متحول ما Fmonth مثلا من أجل القراءة والكتابة , بدون إستخدام أي منهج , القراءة تتم منه والكتابة إلية , وطبعا لا يمكن هنا التأكد من صحة الإدخال , أو إرفاق إدخال أو إخراج القيمة بحدث ما . الحالة الثانية :قمنا بالقراءة من متحول (Fmonth) بشكل طبيعي مثل الحالة الأولى , حيث أنة في كثير من الأحيان لا نحتاج التأكد من صحة الإخراج طالما كنا قد تأكدنا من صحة الإدخال منذ البداية . أما الكتابة فتتم بإستخدام الإجرائية SetMonth , وهنا نستطيع التأكد من صلاحية الأدخال أو إرفاق الإدخال بأحداث ما (إلغاء تعطيل خواص معينة بعد الإدخال مثلا) , وبالطبع هذة الحالة مستخدمة كثيرا على عكس الحالة الأولى . الحالة الثالثة : إستخدمنا التابع GetMonth للإدخال والإجرائية SetMonth للإخراج , وهي الحالة العامة . ملاحظة : قراءة الخاصية ستعيد قيمة واحدة منها , وبالتالي من المثالي هنا إستخدم تابع (Function) للقراءة . الكتابة لن تعيد قيم ولكنها ستدخل قيمة ضمن بارامترات المنهج , لذلك نستخدم إجرائية (Procedure) للكتابة . عادة تكون حقول البيانات ومناهج الدخول السابقة Private (ومن الممكن أن تكون Protected) بينما تكون الخصائص Public . وهذا يعطي درجة مثالية من التغليف, لإنك تستطيع تغيير بيانات الصنف أو مناهج القراءة والكتابة (والتي هي غير مرئية لمستخدم الصنف ) دون أن يتأثر بها مستخدم الصنف ولن يضطر لتغيير شفرتة لإنة يستخدم أسماء الخواص فقط و التي بقيت ثابتة , في حين أن كل التغيرات في طريقة القراءة والكتابة لن تؤثر علية .. تذكر الصندوق الأسود , المستخدم يملك إسم الخاصية ويتعامل معها , طالما بقي إسم الخاصية ثابتا فإن عملة لن يتأثر بأي تغيير .

إضافة تعليق جديد

لا يسمح باستخدام الأحرف الانكليزية في اسم المستخدم. استخدم اسم مستخدم بالعربية

Restricted HTML

  • وسوم إتش.تي.إم.إل المسموح بها: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • تفصل السطور و الفقرات تلقائيا.
  • Web page addresses and email addresses turn into links automatically.

دعوة للمشاركة

موقع الأيهم صالح يرحب بالمشاركات والتعليقات ويدعو القراء الراغبين بالمشاركة إلى فتح حساب في الموقع أو تسجيل الدخول إلى حسابهم. المزيد من المعلومات متاح في صفحة المجتمع.