الجزء الأول :
غرضية التوجة في دلفي 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- أو نسب الغرض إلى غرض موجود تم حجز الذاكرة له مسبقا .. (بإستخدام النسب := )
في هذا المثال قمنا بتعريف غرضين Obj1, Obj2 من الصنف Tclass , الغرض الأول obj1 قمنا بإنشاءة بالطريقة الأولى بإستخدام المنهج المهم والشائع Create الذي يحجز الذاكرة لة ويهيئة للإستخدام .والغرض الثاني obj2 قمنا بنسبة إلى غرض موجود مسبقا وهو Obj1 , وكلا الطريقتين صحيحتين ومستخدمتين .
مهم : بما أننا قمنا بحجز الذاكرة يدويا بإستخدام المنهج Create , فإنة يتوجب علينا تحريرها يدويا أيضاً , ويتم ذلك بإستخدام المنهج Free . أنصحك بإستخدام معالجة الإستثناءات Try) .... Finaly) لتعريف الأصناف وتحريرها ,
يتبع ..
عروة عيسى 12/8/2004
غرضية التوجة في دلفي 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
المنتديات
التعليقات
Re: تعريف الأصناف في دلفي
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) تحديد أي واحد منهما نريد أن نستدعي , ويقوم بالإستدعاء الصحيح للمنهج الصحيح . سنرى تطبيقات هذة الميزة لاحقا يتبع ...Re: تعريف الأصناف في دلفي
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 . وهذا يعطي درجة مثالية من التغليف, لإنك تستطيع تغيير بيانات الصنف أو مناهج القراءة والكتابة (والتي هي غير مرئية لمستخدم الصنف ) دون أن يتأثر بها مستخدم الصنف ولن يضطر لتغيير شفرتة لإنة يستخدم أسماء الخواص فقط و التي بقيت ثابتة , في حين أن كل التغيرات في طريقة القراءة والكتابة لن تؤثر علية .. تذكر الصندوق الأسود , المستخدم يملك إسم الخاصية ويتعامل معها , طالما بقي إسم الخاصية ثابتا فإن عملة لن يتأثر بأي تغيير .إضافة تعليق جديد