‏إظهار الرسائل ذات التسميات java. إظهار كافة الرسائل
‏إظهار الرسائل ذات التسميات java. إظهار كافة الرسائل

الأحد، 10 أغسطس 2014

مرحباً Swift، ما الذي أخَّرك حتي الآن ؟!

 
في مؤتمر WWDC 2014 الصاخب الذي عقدته Apple في الأيام الماضية كان هناك العديد من المفاجآت، منهن أن نظام mac os x صار قريب الشكل من نظام iOS (و هو الأمر الذي لا أخفي كرهي الشديد له، و ربما أخصص مقالاً قادماً لانتقاد هذا الأمر و أوضح فيه مدي استيائي منه). و لكن كان هناك الكثير من الأمور التي أسعدتني، و علي رأسها كان خبر إطلاق Apple للغة برمجة جديدة تسمي swift ستعمل كبديل للغة Objective-C علي المدي البعيد. و في هذا المقال أنوي أن أكتب بعض الفقرات التي أوضح فيها وجهة نظري لتلك اللغة الجديدة.

لكن يجب أن أنبه في البداية إلي أن الملاحظات التي سأضعها هنا عن swift أتت نتيجة لاطلاع مبدئي علي قواعد اللغة، و علي ما كتبه كتاب تقنيون مشهورون، لذلك ففي الإمكان اعتبار آرائي هنا انطباعاً مبدئياً و ليس حكماً نهائياً إلي حد ما؛ فعلي الرغم من أن لغة swift تعجبني من الناحية التصميمية إلي حد كبير، و أنني قرأتُ الكثير عنها بما يجعلني أفهم إلي حد كبير المبادئ التصميمية التي كانت وراء تصميمها بذلك الشكل، إلا أنه لا يمكن إعطاء حكم نهائي علي لغة برمجة تعلق عليها شركة مثل Apple آمالها في الفترة القادمة إلا باطلاع مكثف للغاية، و ربما لو استطعتُ تجربة اللغة بشكل أو بآخر فسيكون هذا ممتازاً.

هل يمكن لبرمجيات الـNet. أن تصير محمولة كبرمجيات الـjava ؟

كما أفضتُ في الشرح في أحد المقالات السابقة، فإن من أكثر الأمور التي تجعلني أحب لغة الـjava أن البرامج المكتوبة بها لها القدرة علي العمل علي أنظمة التشغيل الشهيرة كلها بدون أي تعديل في الأكواد الرئيسة. بالطبع سيكون لزاماً عليك في أكوادك أن تراعي أن تلك الأنظمة تختلف فيما بينها في بعض الأمور، مثل مسارات الملفات و المجلدات، و بالتالي قد يكون مسار مجلد برنامجك (بعد أن يتم تنصيبه) في أنظمة الـWindows كالتالي:

C:\Windows\programfolder

بينما يكون علي أنظمة التشغيل شبيهة اليونكس unix-like (مثل توزيعات الـGNU\linux، و الـmac os x) كالتالي:

/usr/bin/programfolder
و هو ما يعني أنه يجب عليك لكي يكون برنامجك محمولاً portable أن تنتبه إلي مثل هذه الأمور جيداً، و أن تحتاط لها في أكواد البرنامج. و لكن بشكل عام فإن نفس الأكواد التي كتبتَها بالـjava ستعمل علي كل أنظمة التشغيل، و نفس ملفات الـjar (التي تمثل الملفات التنفيذية لبرامجك) ستعمل علي كل أنظمة التشغيل بلا مشاكل.

الاثنين، 3 يونيو 2013

أيهما أنسب للمبتدئين: #C أم java ؟

سألني أحد الإخوة الأفاضل السؤال التالي (ببعض التصرف):

السلام عليكم، 
هناك شئٌ يُحيرني بشدة: فقد اشتركتُ في دورة microsoft الخاصة بالمدارس الثانوية، و هذه الدورة في لغة #C و بعض تقنيات الـNET. الخاصة بـmicrosoft، و لا أعرف هل أُكمِل هذه الدورة أم لا؛ أقصد هل أتعلَّم لغةً مغلقة المصدر من microsoft أم أتحول الي لغةٍ مفتوحة المصدر كـjava.

و كان ردي المُختصَر عليه كما يلي (ببعض التصرف):

الخميس، 28 مارس 2013

طرقٌ مختلفةٌ لدعم الأحداث events و مُتعهِّداتها events handlers

في لغات البرمجة هناك مفهومان هامان للغاية هما: الأحداث events و مُتعهِّدات الأحداث events handlers؛ و يُقصَد بالحدث "وقوع أمرٍ ما يحتاج لاتخاذ رد فعلٍ مناسبٍ تجاهه"، و مُتعهِّد الحدث هو "رد الفعل الذي يرغب المبرمج في تنفيذه عند وقوع حدثٍ معين"، 
و تزداد أهمية الأحداث و مُتعهِّداتها أضعافاً مُضاعفةً حينما يأتي ذِكر برمجة الواجهات الرسومية graphical user interfaces لما تلعبانه من دورٍ مِحوريٍ في تسهيل بنائها؛ فالحاجة إليهن ماسَّةٌ نظراً لأن الفكرة الرئيسة من وراء الواجهات الرسومية هي القدرة علي اتخاذ ردود أفعالٍ معينةٍ عند حدوث شيءٍ معين، مثل عرض رسالةٍ حينما يضغط المستخدم علي زرٍ معين، أو إغلاق البرنامج حينما يضغط المستخدم علي زر x الموجود في الواجهة الرئيسة لذلك البرنامج.

و عملياً فإن هناك طرقٌ عديدةٌ لبناء مفهومَيْ الأحداث و مُتعهِّداتها في لغات البرمجة، و هنا سأقدم مجموعةً صغيرةً للغاية منهن، ثم سأتحدث عن الطريقة التي تم بها الاستغناء عنهن في إبداع بشكلٍ بسيطٍ جداً و قوي للغاية. 

إلا أنه ينبغي التنبه إلي كون الشرح الموجود هنا مُختصَرٌ بشكلٍ كبير، و يحتاج كذلك لمعرفةٍ جيدةٍ ببعض قواعد لغات الـ: java و #C و visual basic .NET و إبداع. و ربما يتسبب هذا في غموض بعض النقاط و الحاجة إلي القراءة المستفيضة فيهن حتي يتم استيعابهن. و لم أستطع الإسهاب في الشرح حتي لا يزداد حجم المقال إلي ما لا يُطاق.

الأحد، 10 فبراير 2013

استبدال الإجراءات متعددة الخرج بالإجراءات الداخلية

هناك لغات برمجةٍ تسمح بتفريع الإجراءات functions nesting، أي أنها تسمح بتعريف إجراءٍ أو أكثر داخل إجراءٍ آخر، و من تلك اللغات لغة الـpython علي سبيل المثال. و في أمثال تِلكم اللغات نري أننا نحصل علي فائدةٍ جميلةٍ من تفريع الإجراءات، حيث أن الإجراءات الداخلية يمكن أن تكون قادرةً علي التعامل مع المتغيرات و الثوابت و ما يشبههن اللاتي تم تعريفهن في الإجراءات الخارجية قبل تعريف الإجراء الداخلي، و كذلك التغيير في قِيم تلكم العناصر بحرية.
كما في المثال التالي المكتوب بالـpython:
def  func1():
    x = [10]
    y = [20]
   
    def  func2():
         x[0] = x[0]+ 1
         y[0] = y[0] + 1
   
    func2()
   
    print x[0],"    ", y[0]
   
func1()

الذي يعطي عند تنفيذه الخرج التالي:
11         21

و لو حاولنا إعادة صياغة البرنامج السابق بلغة لا تدعم تفريع الإجراءات مثل الـjava لوجدنا أننا سنضطر لكتابة كودٍ يشبه ما يلي:

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        t.func1();
    }
   
    void func1() {
        int x = 10, y = 20;
        result res = func2(new result(x, y));
        System.out.println("" + res.x + "    " + res.y);
    }

    result  func2(result res){
        res.x = res.x +1;
        res.y = res.y +1;
        return res;
    }

    class result {
        int x, y;
        public result(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
}
و نري فيه أننا اضطررنا إلي عمل صنفٍ جديدٍ result يحوي داخله البيانات المتعددة التي نرغب في إعادتها لمحاكاة عمل الإجراءات الداخلية. و الأمر في المثال السابق يتعلق بمحاكاة عمل إجراءٍ داخليٍ واحد، فكيف يكون الحال مع محاكاة عمل أكثر من إجراءٍ تتعامل مع أكثر من من حاويةٍ داخلية ؟!
بالطبع ستكون هذه من الأمور المرهقة للمبرمج، و كلما ازدادت المشاريع البرمجية ضخامةً كلما قابل المبرمج مواقفاً تشبه السابقة و لكنها أكثر تعقيداً.
لكن هذه المشكلة يمكن حلها بمنتهي البساطة إذا كانت الإجراءات قادرةً علي إعادة أكثر من خرج، كما هي الحالة مع لغة إبداع، و بالنظر إلي بناء المثال السابق بإبداع:


إجراء1()


إجراء  إجراء1 :
    رقم س = 10
    رقم ص = 20
   
    س ، ص = إجراء2(س    ص)
   
    أكتب.نص.سطر(إلي.نص(س) +"       " + إلي.نص(ص))
   
إجراء  (رقم س2  رقم ص2)  إجراء2(رقم س1  رقم ص1):
    س2 = س1 +1
    ص2 = ص1 +1

   
   
و له تقريباً نفس عدد أوامر البرنامج المكتوب بالـpython، بل و يمكن اختصار البرنامج السابق أكثر من ذلك بجعل كود الإجراء إجراء2 كما يلي:

إجراء  (رقم س2=س1+1 رقم ص2=ص1+1)  إجراء2(رقم س1  رقم ص1):
    لاشيء

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

كما أنه يمكن الاستغناء عن الإجراءات الداخلية عن طريق التمرير بالمرجع passing by reference، و هو الأمر الموجود في لغاتٍ كثيرةٍ منها الـC  و الـ++C و الـ object pascal و الـD و غيرهن من اللغات الأخري. و يمكننا كتابة المثال السابق بلغة object pascal كما يلي*:

function func2(var x, y :integer);
begin
    x = x+1
    y = y+1
end;

function func1();
begin
    x :integer = 10;
    y :integer = 20;
   
    func2(x,y);
   
    writeln(x +"    " +y);
end;

begin
    func1();
End.

و بالطبع يمكنكم ملاحظة أنه تم الاستغناء عن الإجراءات الداخلية هنا أيضاً بكفاءة، لكن أيضاً يمكنكم ملاحظة أن تعقيد قواعد الـobject pascal جعل الأمر أقل سهولةً من حالتَيْ الـpython و إبداع.

كما أن هناك نقطةً أخري تتعلق بإمكانية حدوث ارتباكاتٍ و مشاكلٍ منطقيةٍ في اللغات التي تدعم الإجراءات الداخلية، حينما تكون تلك اللغات من نوع اللغات استنتاجية الأنواع
Type inference، فمثلاً لو نظرنا إلي البرنامج الأول المكتوب بلغة الـpython لوجدنا أنه يستخدم الـlists و ليس المتغيرات الرقمية البسيطة، فلماذا لم أكتبه كما يلي:
def  func1():
    x = 10
    y = 20
   
    def  func2():
         x = x + 1
         y = y + 1
   
    func2()
   
    print x,"    ", y
   
func1()

علي الرغم من أن هذا هو الأمر الأقرب للمنطق ؟!
الجواب أنني حينما فعلتُ ذلك أَخرجَ لي مُفسِّر الـpython الخطأ التالي:
x = x + 1                                                                                                            
UnboundLocalError: local variable 'x' referenced before assignment
و السبب في هذا أنه اعتبر أن المتغيرين x و y داخل الإجراء func2 يختلفان عن المتغيرين x و y اللذين في func1 ! و بالتالي أصبح من الخطأ كتابة الأمرين:
x = x + 1
y = y + 1
لأننا بهذا نكون قد استخدمنا المتغيرين x و y قبل إعطائهما أي قيمة، رغم أن المبرمج يقصد أن يستخدم x و y الخاصين بالإجراء func1 و اللذان تم إعطاؤهما قيماً ابتدائيةً هن 10 و 20 علي الترتيب !

و الغريب أنك لو عرَّفتَ list داخل func1 فيمكنك استخدام عناصرها الداخلية بشكلٍ عاديٍ في func2، و هو ما لجأتُ إليه فعلاً في المثال الأول. و لكنك لو أسندتَ قيمةً لاسم تلك الـlist في الإجراء الداخلي
func2 فإنه سيعتبر أنك تقوم بتعريف list جديدة داخل func2 تُغطِّي تلك التي تحمل نفس الاسم في func1 !
أي أنهً عند تنفيذ المثال التالي:

def  func1():
    x = [10]
    y = [20]
   
    def  func2():
         x = [11]
         y = [21]
   
    func2()
   
    print x[0],"  ",  y[0]
   
func1()
تكون النتيجة:
10    20

لأن القِيم [11] و [21] أُسنِدت إلي x و y  الخاصَّيْن بـfunc2، بينما بقي x و y الخاصَّيْن بـfunc1 كما هما، و هذا يعني معضلةً عند محاولة المبرمج التعديل في قيمة x أو y  في func1 من داخل func2؛ حيث أنني حينما حاولتُ إيجاد طريقةٍ لفعل ذلك تختلف عن استخدام خاصية الخرج المتعدد: لم أجد، و هو ما يؤيد عدم الحاجة الضرورية لتفريع الإجراءات، و أن كل الفوائد التي يمكن جنيها من ورائه يمكن الحصول عليها بشكلٍ أبسط و أسهل عن طريق الإجراءات متعددة الخرج. 
كما أن هذا الأمر يؤكد أيضاً ما ذهبتُ إليه من أفضلية فرض التعريف اليدوي للأنواع علي المبرمج و عدم جعل اللغة من النوع الذي يستنتج الأنواع بدون تدخل المبرمج**.


----------------------------------
* كل البرامج المكتوبة في هذا المقال تم تنفيذها عملياً و التأكد من خلوها من الأخطاء، ما عدا البرنامج المكتوب بالـobject pascal.
** تجدون كلامي عن هذه الجزئية في فصل (فائدة تعريف الحاويات يدوياً) الموجود في باب (أين الإبداع فيها؟) في نهاية كتاب (رسالة البرمجة بإبداع).

الجمعة، 7 ديسمبر 2012

نبذةٌ عن مكتبتَيْ الـawt و الـswing

هذه نبذةٌ مُختصَرةٌ عن مكتبتَيْ الـawt و الـswing البرمجيَّتَيْن، كتبتُها كَرَدٍ علي سؤالٍ في المنتدي العلمي الوحيد الذي أُتابِعه، و رأيتُ أن  أُنقِّحها بعض الشيء ثم أضعها في المدونة للاستفادة العامة.
 ***
الـawt و الـswing باختصارٍ هما مكتبتان برمجيتان تُتيحان إنشاء واجهاتٍ رسوميةٍ GUI للبرامج، مثل النوافذ windows و الأزرار buttons و صناديق النصوص text boxes و غيرهن من المُكوِّنات الرسومية الأخري، و تُعتبَران جزءاً مما يُسمَّي بإطار الـJava Foundation Classes أو الـJFC اختصاراً، و هو إطارٌ برمجيٌ يُوفِّر واجهاتٍ برمجيةٍ قياسيةٍ لإنشاء الواجهات الرسومية للغة الـjava بغض النظر عن نظام التشغيل الذي يعمل عليه البرنامج.



الـawt :
هي مواصفاتٌ يتم بناؤها بشكلٍ مختلفٍ لكل نظام تشغيل يتم دعمه فيها، يعني: حينما يتم تنفيذ  البرنامج الذي يستخدم مكتبة الـawt علي نظام تشغيل الـwindows فإن نسخة مكتبة الـawt التي ستعمل ستكون في هذه الحالة مبنيةً علي مكتبات الـwindows نفسه، و لو تم تنفيذ ذات البرنامج علي نظام القنو/لينوكس GNU/linux فستكون نسخة الـawt التي ستعمل في هذه الحالة مبنيةً علي إحدي المكتبات المتوافرة له.

و لأنها تستخدم مكتبات أنظمة التشغيل فإن شكل الأزرار و بقية المُكوِّنات في القنو/لينوكس يشبه شكلها في بقية البرامج، و شكل الأزرار و المكونات الأخري في
الـwindows مشابهٌ لشكل بقية البرامج، أي أن البرنامج يختلف مظهره باختلاف نظام التشغيل الذي يعمل عليه، 

مثالٌ لشكل برامج الـawt علي الـwindows (من صفحة الـawt علي الويكيبيديا)

بعض الناس يرون هذا ميزةً و بعضهم يراه عيباً، أنا عن نفسي أري أنه ميزة.
و لكن نتيجةً لكونها تعتمد علي مكتبات أنظمة التشغيل فقد ورثَتْ منها مشاكلها التي توجد بها، و بالتالي اشتُهِرت بأنها غير مستقرةٍ بما فيه الكفاية و ينتج عنها أغلاط زمن تشغيلٍ runtime errors كثيرة (و قد جرَّبتُ هذا بنفسي)، و أظن أن هذه المشاكل زادت بشكلٍ كبيرٍ بعد أن تم استبدالها بمكتبة الـswing و تم إهمال الـawt ربما حتي علي مستوي إصلاح العِلل !
 

الـswing:
هي مكتبةٌ مُشابِهةٌ جداً للـawt لأنها صُنِعت كبديلٍ مُتطوِّرٍ لها، و لكنها تتلافي العيوب الموجودة في الـawt عن طريق البعد عن الإعتماد التام علي مكتبات أنظمة التشغيل، و بالتالي تكون البرامج المصنوعة  بالـswing منفصلةً عن مكتبات أنظمة التشغيل و تصبح مستقرةً جداً و لها نفس البناء علي كل تلك الأنظمة.
و لأنها منفصلةٌ عن مكتبات الأنظمة المختلفة فإن مظهر البرامج التي تُكتَب بها يكون واحداً عليها كلها، 


مثالٌ لشكل برامج الـswing علي مُختَلف أنظمة التشغيل (من صفحة الـswing علي الويكيبيديا)

و من جديد: فبعض الناس يري هذا عيباً و بعضهم يراه ميزة، و أنا عن نفسي أعتبره عيباً لأنه يجعل برامجها في الـwindows مختلفة المظهر عن بقية البرامج بشكلٍ واضح، و كذلك نفس الأمر علي القنو/لينوكس، مما يسبب بعض النفور من اختلاف المظهر (علي الأقل عندي)، و لكني أُقِرُّ أن هذا الشعور يتلاشي مع مرور الزمن و يعتاد المستخدِم علي الشكل المختلف للبرنامج، و قد جَرَّبْتُ هذا مع برنامج الـnetbeans الذي له نفس الشكل المختلف و أصبحتُ لا أحسُّ بأي نفورٍ من هذه الناحية.
 
بالنسبة للأفضلية: 
فالـawt أفضل من حيث السرعة في التنفيذ،
و من حيث قلة المشاكل و التطور و الإصلاح الدوري للعِلل فالـswing أفضل،
أما من حيث السهولة فمن خلال استخدامي البسيط لهما فكلاهما له نفس الدرجة من السهولة.


التكوين العام للمكتبتَيْن (من صفحة مكتبة الـswing علي ويكيبيديا)

للمزيد من التفاصيل التقنية و الأمثلة العملية يمكن زيارة صفحتَيْ الويكيبيديا الخاصتين بالمكتبتين:
http://en.wikipedia.org/wiki/Abstract_Window_Toolkit
http://en.wikipedia.org/wiki/Swing_%28Java%29

بالمناسبة: هناك مكتبةٌ ثالثةٌ تُحاوِل دمج ميزات المكتبتَيْن السابقتين و تلافي عيوبهما، و هي مكتبة الـswt التي يمكن قراءة المزيد عنها علي صفحة الويكيبيديا الخاصة بها:
http://en.wikipedia.org/wiki/Standard_Widget_Toolkit

و قد كانت لي تجربةٌ مع الـawt و الـswing و انتقلتُ من استخدام الأولي إلي استخدام الثانية، و يمكن قراءة المزيد عن هذا علي الرابط التالي:
http://afkar-abo-eyas.blogspot.com/2012/08/awt-swing.html

السبت، 25 أغسطس 2012

مقدمة عن البرمجة الكائنية object oriented programming OOP


مقدمةٌ عن البرمجة الكائنية
 object oriented programming 
OOP


الشرح التالي هو تعديلٌ غير كبير لشرحٍ كنتُ قد كتبتُه لمجموعةٍ من زملائي في الكلية (في السنة قبل الأخيرة لنا فيها)، أشرح فيه نمط البرمجة المسمي بالبرمجة المَقُودة بالكائنات object oriented programming أو اختصاراً (البرمجة الكائنية). و قد وجدتُه بعد كل تلك الفترة التي نسيتُه فيها بسيطاً و ربما جيداً، فقلتُ أضعُه للناس ليستفيدوا منه. و ربما أقوم في المستقبل بإذن الله تعالي بتكملته و/أو التعديل فيه.

ماهية البرمجة الكائنية:

لكي نفهم ماهية و خصائص نمط البرمجة الكائنية oop paradigm يجب علينا أن نتفهم المثال التالي:
فلنفترض أننا نريد كتابة برنامجٍ يقوم بإنشاء خَمس حساباتٍ لخمسة أشخاصٍ في خمسة شركاتٍ مختلفة، و يقوم بحساب مستحقاتهم المالية لدي الشركة بعد مرور ثلاث سنوات بمعرفة نسبة الربح السنوي المعتادة من رأس المال. مع ملاحظة أن الكود سيكون بنمط البرمجة العادي (أي بدون استخدام الأصناف classes و الكائنات objects).

double account1 = 100, profit1 = 0.1;
double account2 = 150, profit2 = 0.15;
double account3 = 200, profit3 = 0.20;
double account4 = 175, profit4 = 0.25;
double account5 = 200, profit5 = 0.30;


double after_years(double old_account, double profit, int years){
for(int i = 1; i <= years; i++){
old_account = old_account * profit + old_account;
}
return old_account;
}

void main(){
account1 = after_years(account1, profit1, 3);
account2 = after_years(account2, profit2, 3);
account3 = after_years(account3, profit3, 3);
account4 = after_years(account4, profit4, 3);
account5 = after_years(account5, profit5, 3);
}


بملاحظة البرنامج السابق سوف نري أننا لتمثيل كل حسابٍ من الحسابات  المالية استخدمنا متغيرين من نوع double: 
  • الأول لتمثيل النقود التي يحتويها الحساب  account
  • الثاني لتمثيل نسبة الربح profit
و تم  تكرار هذا مع كل حسابٍ من الحسابات المالية، بالطبع مع تغيير أسماء المتغيرات للتمييز بينها.
إذاً فالسؤال المنطقي الآن: هل يمكنني كمبرمجٍ أن أختصر كل ذلك الكود ما دام الكثير منه مكرراً بالفعل ؟

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

و بالتطبيق علي المثال السابق نري أن التقسيم الأَوَّلي لما سنحصل عليه سيكون كالتالي:
double account, profit;
double after_years(double old_account, double profit, int years){
for(int i = 1; i <= years; i++){
old_account = old_account * profit + old_account;
}
return old_account;
}

يمكن أن نُطلق علي الكود السابق إسم caccount اختصاراً لـcompany account و هو اسمٌ معبرٌ جداً عن وظيفته.
و في البرنامج الأساسي سنكتفي بطلب نسخ الكود caccount خمس مرات من مترجم اللغة بأسماء account1  account2   account3   account4   account5.
caccount account1;
caccount account2;
caccount account3;
caccount account4;
caccount account5;

فماذا سيفعل الحاسوب بعد حجز أماكن لتلك الكائنات في الذاكرة ؟
سوف يقوم بعمل ما يلي تقريباً:



و بداخل كل وحدةٍ من الوحدات المحجوزة تُوجد المتغيرات الموجودة في الكود المُكرر الذي كتبناه من قبل و وضعناه جانباً.
فلو كتبنا في البرنامج:
 account1.account
فإننا نعني المتغير account الذي هو من نوع double و المحجوز للنسخة المُسماة account1.
و هكذا مع:
account2.account

و غيرها.

و لو أردنا أن ننفذ دالة علي النسخة account1 بحيث تعملُ باستخدام متغيراتها الخاصة فإننا نكتب الكود التالي:
account1.methodname();

مثال:
account1.newaccount();


إلي الآن و الأمر مفهوم: يوجد لدينا كود مكرر في البرنامج فقمنا بكتابته مرةً واحدةً و أطلقنا عليه اسماً مُعيناً، ثم صنعنا منه ما نريد من نسخ.
و لكن هل هذا كل شيء ؟

لا.
فماذا لو أنني أردتُ إعطاء قيم ابتدائية للمتغيرات الموجودة داخل النسخ الجديدة في نفس سطر تعريف كل نسخةٍ منها، كيف يمكنني فعل هذا ؟
و ماذا لو أنني أردتُ منع الوصول المباشر للمتغيرات داخل النسخ، مثل الأمر:
account1.account = 1000;

و أردتُ أن تكون هذه المتغيرات متاحةً فقط للدوال الموجودة معها في الكود المُكرر فقط ؟
إلي آخر تلك الأسئلة التي تُوضح إجاباتُها خصائصَ نمط البرمجة الكائني.

سوف نقوم بتوضيح إجابات تلك الأسئلة فيما يلي، و لكن أولاً سوف نقوم بشرح المُصطلح العلمي المقابل لبعض الكلمات التي ذكرناها من قبل أثناء الشرح:



التوصيف المُصطلح العلمي العربي المُصطلح العلمي الإنقليزي
كود مكرر صِنْف class
نُسخة نسخة، كائن Object, instance
متغير داخل صنف حقل بيانات Data field
دالة داخل صنف دالة حالة Instance method

و لنجب الآن عن الأسئلة التي سبق و طرحناها.
أما كيفية إعطاء حقول البيانات قيماً ابتدائية في نفس جملة التعريف فيتم عن طريق ما يُسمي بالمُشَيِّد constructor، و هو دالةٌ لها نفس اسم الصنف class بدون أي نوعٍ من المُخرجات (ليس حتي void).
و ينقسم المُشَيِّد إلي نوعين:
  • المُشَيِّد الافتراضي default constructor
  • المُشَيِّد ذو المُدخَلات parameterized constructor

و هذا هو المثال السابق بالكامل (مع إضافة مُشَيِّداتٍ من ذوات المُدخَلات):
public class caccount {
double account, profit;

public caccount(double account, double profit){
this.account = account;
this.profit = profit;
}
public static void main(String[] args){
caccount account1 = new caccount(100, 0.1);
caccount account2 = new caccount(150, 0.15);
caccount account3 = new caccount(175, 0.20);
caccount account4 = new caccount(200, 0.25);
caccount account5 = new caccount(250, 0.3);
}

double after_years(double old_account, double profit, int years){
for(int i = 1; i <= years; i++){
old_account = old_account * profit + old_account;
}
return old_account;
}
}

و يقوم البرنامج بحجز مكانٍ في الذاكرة للكائن الجديد ثم تنفيذ المُشَيِّد الافتراضي لو كتبنا ما يلي:

caccount  account1 = new  caccount();

و لاحظ عدم وجود قيم بين قوسي المُشَيِّد المُستدعَي.
بينما يقوم البرنامج بحجز مكان الذاكرة ثم تنفيذ المُشَيِّد ذي المُدخلات لو كتبنا ما يلي:
caccount  account1 = new  caccount(100, 1.0);

و لاحظ وجود قيمٍ بين قوسي المُشَيِّد المُستدعَي. و في هذه الحالة يكون:
account2.account = 100
account2.profit = 0.1

أما منع الوصول المٌباشر لحقول البيانات فيكون ببساطة بإعطائها الخاصية private عند تعريفها، مثل:
private double account;

و لو أننا قمنا بتعريف كل حقول البيانات الموجودة في الصنف و أعطيناها الخاصية private فسنجد أننا وصلنا إلي جعل الصنف أشبه بالكبسولة؛ حيث لا يُمكن الوصول لحقول البيانات إلا من خلال الدوال الموجودة في الصنف، و هو ما يُعرف بالكبسلة encapsulation.

و كذلك ينطبق مفهوم الكبسلة علي كود الدوال الموجودة داخل الصنف، حيث لا يُمكن للمستخدم في البرنامج النهائي أن يُغير من ذلك الكود، بل هو مجرد مستخدمٍ له فقط.

أين تتم كتابة الأكواد:

في هذه المرحلة نكون قد وصلنا إلي تصورٍ جيدٍ لمفهوم البرمجة الكائنية oop و كيفية الاستفادة منها في تصغير حجم البرنامج و حماية حقول البيانات من تدخل المستخدم النهائي، و لكننا حتي الآن لا ندري أين تتم كتابة كود الصنف الفرعي و أين سنكتب الكود الذي سيستخدم هذا الصنف ؟
و هذه نقطةٌ هامةٌ للغاية تختلف فيها لغات البرمجة؛ ففي لغة الـ++C مثلاً سوف نجد أننا سوف نستخدم ملفات الـheader files (أو ملفات التَرْويسَة ) لكي نكتب فيها كود الـصنف و يكون لها الامتداد (h.)، و سيكون علينا أن نكتب الكود بنفس طريقة الـ++C التي تشترط كتابة مُلخص الصنف specification ثم بعد ذلك يأتي كوده التفصيلي (أو ما يُسمي بالبناء implementation).
و كذلك فإنه يمكننا أن نكتب التلخيص في ملف ترويسة و البناء داخل ملف آخر له الامتداد cpp. مع كتابة الجملة التالية في بداية هذا الملف الجديد:
#include "caccount.h";

لجعل المُترجم يفهم أن الملفين مرتبطان ببعضهما البعض.
هذا بالنسبة للـ++C، أما في حالة الـjava فيمكننا كتابة كود الصنف في نفس ملف البرنامج الأصلي، أو في ملفٍ مستقل لكن بحيث:
  • يشتمل كل ملفٍ مستقلٍ علي صنفٍ واحدٍ فقط يماثله في الاسم، و بإمكاننا كتابة أصنافٍ أخري داخل ذلك الصنف الواحد.
  • يكون امتداد الملف المستقل(java.)

مفهوم الوراثة inheritance و علاقته بمبدأ إعادة الاستخدام:

بعد الانتهاء من كتابة برنامجٍ ضخم بنمط البرمجة الكائنية سوف نجد أنه قد تم كتابة عددٍ كبيرٍ من الأصناف المُستقلة التي سهلت علينا العمل و أسرعته بمعدلاتٍ فائقة.
لكن السؤال الحيوي هنا: هل يمكنني الاستفادة منها بصورةٍ أكبر ؟

بالطبع نعم؛ حيث يمكنني إعادة استخدام reuse هذه التصنيفات الموجودة عندي في كتابة برامجي الجديدة دون الحاجة إلي إعادة كتابة كودها مرةً أخري أي أنني أصنع مكتبتي الخاصة بي، تماماً كالمكتبات القياسية للغات البرمجة، حيث أن الأصناف التي نستخدمها في برامجنا التي نكتبها بتلك اللغات ليست إلا أكواداً قام بكتابتها متخصصون في شركة البرمجيات و أراحونا من عناء كتابتها منذ البداية.
و علي سبيل المثال لو أنني احتجتُ للصنف caccount أثناء كتابتي لبرنامجٍ جديدٍ بلغة الـ++C فسوف أقوم فقط بضمه للبرنامج بالعبارة:
#include "caccount.h";

 و بذلك يمكنني استخدامه بمنتهي الحرية.

لكن ماذا لو أردتُ استعمال هذا الصنف مع إحداث بعض التغييرات فيه (كتابة حقول بياناتٍ إضافية، أو كتابة دوالٍ إضافية، أو حتي تعديل أكواد دوالٍ موجودةٍ من قبل) مع الاحتفاظ بهيكل و مكونات الصنف الأساسي كما هي، فما العمل ؟
 الحل هنا يكمنُ في مفهوم الوراثة؛ فحينما أُخبر اللغة أنني أريد كتابة تصنيفٍ جديدٍ فلنفترض أنه new_caccount يرث الصنف caccount فإنه يكون مفهوماً لديها أن الصنف الجديد يحتوي علي كل حقول البيانات و الدوال التي يحتويها الصنف القديم مع وجود إمكانية التعديل كما سبق و شرحنا.
و تختلف اللغات فيما بينها في مدي و كيفية الوراثة: أهي وراثة متعددة multiple inheritance بما يعني أن الصنف الواحد يمكنه وراثة عددٍ غير محدودٍ من التصنيفات الأخري كما في الـ++C و الـeiffel، أم هي وراثة أُحادية أي أن الصنف لا يمكنه وراثة أكثر من صنفٍ واحدٍ فقط كما في لغات الـjava و الـ#C. و ربما نتحدث عن هذا بالتفصيل في شروحاتٍ قادمةٍ بإذن الله تعالي.

إلي هنا نكون قد أنهينا المفاهيم الأساسية في عالم البرمجة الكائنية، و لكن أشياء كثيرة للغاية ما زالت تنتظر و لكنها تتأثر بماهية اللغة التي سنبرمج بها، لذلك ستكون هذه الأشياء في الـ++C مختلفة الشكل و الخصائص عنها في الـjava و عنهما في الـ#C.
و منها:
  • الواجهات interfaces
  • الهياكل structs
  • التعدادات enumerations
  • الوسائط delegates
  • الاحداث و تعهد الأحداث events and events handling
و غيرهن.