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

السبت، 24 يناير 2015

نصيحة ذهبية: قلل اعتماديات برامجك التي لا لزوم لها قدر الإمكان

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

في البداية فإن من ضمن الاعتماديات التي أتحدث عنها (و النقطة التي سنركز عليها في هذا المقال) أن يكون برنامجك يستعين بمكتبات إضافية في عمله ليست من برمجة المطورين القائمين عليه، سواء أكانت تلك المكتبات مفتوحة المصدر أو مغلقة المصدر. كأن تريد إضافة ميزة الـOCR (اختصار لـOptical character recognition) إلي أحد برامجك، و لأن هذه ميزة معقدة للغاية، فبالتالي سيكون من الأفضل بكثير أن تستعين بمكتبة خارجية لتقوم بتلك العملية، مثل مكتبة tesseract-ocr، و هكذا يمكنك أن تركز جهدك كله علي الأمور التي يتميز بها برنامجك بدون الحاجة لبناء ما بناه الآخرون بالفعل، كما أنك بهذه الطريقة ستستفيد في المستقبل من التحسينات و الإصلاحات التي يطبقونها علي مكتبتهم بدون أي جهد من ناحيتك.
 
 

الأربعاء، 5 مارس 2014

مقدمة سريعة عن الحوسبة و البرمجة

توطئة:
 
هذه مقدمة بسيطة مختصرة تصلح لشرح الفكرة العامة لبعض الأمور و المفاهيم الأساسية المتعلقة بالحوسبة computing و البرمجة programming، و الهدف الرئيس من ورائها أن تكون هي نفسها مجرد توطئة للدخول فيما بعد إلي عالم التفاصيل التقنية المتخصصة رويداً رويدا. و لذلك حرصتُ أن تكون خاليةً قدر الإمكان من أية تفاصيل تقنية تتطلب أي نوع من أنواع المعرفة السابقة، مع الحرص علي تشبيه معاني المفاهيم و المصطلحات الحاسوبية المذكورة هنا بأشياء محسوسة من الواقع؛ لتكون الأمور أيسر في الاستيعاب حتي بالنسبة لمن ليست له أية خلفية تقنية قديمة *.
 
 

الأحد، 17 نوفمبر 2013

ما هي أفضل لغة برمجة ؟

علي موقع Arabia I/O كان هناك تساؤلٌ يقول فيه صاحبه: "ما هي أفضل لغة لبرمجة تطبيقات سطح المكتب ؟"، فكتبتُ إجابةً مُختصَرةً علي سؤاله تصلح للإجابة عن كل الأسئلة التي من عينة: "ما هي أفضل لغة برمجة لفعل كذا و كذا ؟"، أو حتي للإجابة علي السؤال الأعم و الأشمل: "ما هي أفضل لغة برمجة ؟".

و كان ردي كما يلي (مع تصرُّف بسيط للغاية):

الثلاثاء، 17 سبتمبر 2013

الشكل الأقدم للأحداث و متعهداتهن events and events handling في إبداع و أسباب تغييره

مقدمة

هذا المقال يُعتبَر تكملةً لمقالٍ سابقٍ كتبتُه عن الطرق المُختلفة لدعم "الأحداث events" و "مُتعهِّداتها events handlers" في بعض لغات البرمجة (أعني بهن الـ: java و visual basic.net و #C)، ثم قارنتُ حينها بين كل تلك الطرق و بين الطريقة التي يتم بها الدعم حالياً في إبداع؛ بغرض البرهنة علي قوة و بساطة قواعد الأخيرة التي قد لا تظهر عند التأمل البسيط.

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

الخميس، 12 سبتمبر 2013

الوراثة الجزئية partial inheritance

الوراثة الجزئية partial inheritance
المفهوم، المميزات، العيوب


المفهوم و المميزات

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

و أثناء تلك الفترات التي كانت تتغير فيها قَناعاتي وجدتُ أن هناك بعض المُكوِّنات (أعنيقواعد و تعبيراتكنتُ مقتنعاً بأهميتها في الأشكال القديمة من إبداع، و لكن بعد التفكير الجيد فيها وجدتُ أنه من الأفضل ألا يتم ضمها إلي اللغة في نسختها النهائية.

و كان من بين تلك المُكوِّنات التي تم التخلي عنها ما أطلقتُ عليه اسم "الوراثة الجزئية partial inheritance" أو "الوراثة الناقصة incomplete inheritance"، و هو نوعٌ من أنواع الوراثة في البرمجة الكائنية object oriented يبدو غريباً للغاية عند شرحه (كما سترون فيما يلي من توضيح بمشيئة الله تعالي).

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

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

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

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

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

الأحد، 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.
** تجدون كلامي عن هذه الجزئية في فصل (فائدة تعريف الحاويات يدوياً) الموجود في باب (أين الإبداع فيها؟) في نهاية كتاب (رسالة البرمجة بإبداع).

الأحد، 20 يناير 2013

بسيطٌ و مُعقَّد

أمرٌ مدهشٌ جداً أن تَري أن خاصيتَيْن أساسيتَيْن في مُحررات النصوص مثل خاصية (تراجع undo) و خاصية (كرِّر redo) رغم بساطة مبدأيهما: إلا أنهما يحتاجان لانتباهٍ شديدٍ و تجريبٍ كثيرٍ حيت يتم بناؤهما بشكلٍ صحيح !

السبت، 19 يناير 2013

برنامج (الحاسب) 0.1

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

تسليتُ قليلاً بمشاهدة بعض الفيديوهات للينوس تورفالدز و غيره، لا يزال لينوس تورفالدز قادراً علي إدهاشي بقوةٍ بسبب كمية الثقة بالنفس التي عنده، و تلك السهولة التي يتحدث بها عن إنشاء البرمجيات الضخمة بمفرده (أو علي الأقل بناء المرحلة الأولي منها)، و كان الفيديو الذي شاهدتُ جزءاً منه خاصاً بالمحاضرة التي ألقاها لينوس ضمن محاضرات google tech talk لعام 2007م، و قد سمعتُها بالكامل عدة مراتٍ من قبل !
ثم فجأةً خطرت ببالي فكرةٌ جيدة: لِمَ لا أكتب ذلك البرنامج الذي كنتُ أحتاجه منذ فترةٍ طويلةٍ و بحثتُ عن شبيهٍ له فلم أجد ؟!

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

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

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

و قد جربتُ البرنامج علي الكثير من المشاريع البرمجية، سواءٌ تلك التي كتبتُها أنا أو التي توافر لديَّ كودها المصدري، و منها الكود الخاص بمكتبة الـOpenCV في الإصدارة 2.0 منها، و بالطبع اهتممتُ بإجراء تحليلٍ لمشروع مُفسِّر أُبْدِع كما في الصورة:
و بعد أن انتهيتُ من بناء البرنامج كتطبيقٍ منفصلٍ قمتُ بحمد الله تعالي بدمجه داخل بيئة أندلس في الإصدارة 0.2 التي أعمل عليها هذه الأيام.

و قد كان هذا بعد عناءٍ شديد :)

يمكن لمن يرغب في استخدام البرنامج تحميله من هنا:
علي الـ4shared
علي الـsourceforge



و هناك كتيبٌ لتعليم كيفية تنصيبه و استخدامه، يمكن تحميله من هنا:
علي الـ4shared
علي الـsourceforge


كما أن البرنامج مفتوح المصدر open source لمن يرغب في استعمال أكواده أو البناء عليه، و يمكن تحميل كوده من هنا:
علي الـ4shared
علي الـsourceforge

الجمعة، 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

الخميس، 29 نوفمبر 2012

ملفٌ كاملٌ لكل مشروع

قاعدةٌ اخري من قواعد التنقيح debugging العملية، و لكنها هذا اليوم بسيطةٌ جداً و قد يجد الواحد منكم نفسه يراعيها بالفعل أو لا يحتاج إليها من الأصل.
الحكاية وما فيها أنه حينما تعمل علي مشروعٍ برمجيٍ ضخمٍ (مثلاً مُفسِّرٍ للغة برمجةٍ متقدمة) فيجب عليك أن تقوم بكتابة العلل التي تقابلها أثناء اختباراتك للبرنامج باستمرار و بأسلوبٍ واضحٍ مستفيض؛ حتي يمكنك الرجوع لها بسهولةٍِ فيما بعد لترتيب أولويات عملك في المراحل القادمة، و لتغطية جوانب النقص و حل المشاكل التي تظهر في عمل البرنامج.
هذا أمرٌ طبعيٌ، أليس كذلك ؟

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

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

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

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

الثلاثاء، 27 نوفمبر 2012

لا تحتفل مبكراً

قاعدةٌ جديدةٌ من قواعد التنقيح debugging التي استفدتُها من خلال الواقع العملي هي ما يمكن تسميته بقاعدة (لا تحتفل مبكراً) أو (كن مُرتاباً)، بمعني أنه يجب ألا تفرح بإصلاح أي علةٍ bug من العلل قبل أن تتأكد من أنك قد حللتها بالفعل؛ فربما تظن أنك قد حللت تلك العلةً بشكلٍ نهائيٍ بينما لم تَحُل إلا حالةًواحدةً من الحالات التي تظهر فيها، 
و هذا سيؤدي بك إما إلي الفرح بالإصلاح المزعوم و بالتالي تغفل عن الحالات التي لم تكتشفها لاطمئنانك المُتعجَّل، و من ثم قد تتحول حياة مستخدمي تطبيقك إلي جحيمٍ مستعر، كما أنه من الممكن أن يكون لإصلاحك للعلة الأولي آثارٌ جانبيةٌ أخطر منها و يجب تداركها بأسرع ما يمكن.
أو سيؤدي بك إلي الاكتئاب البالغ إذا ما اكتشفتَ بعد المهرجان الذي أقمتَه (خصوصاً لو كانت هي العلة التي تفصلك عن الإصدارة المستقرة) أن الأمور لا تزال تحتاج إلي عملٍ ربما يتضح أنه أكبر مما حللته بمراحل، 
و هذا الاكتئاب قد يجعلك تُوقِف العمل فترةً غير هينةٍ لعدم قدرتك النفسية علي تحمل ضياع فرحة الانتهاء، أو علي الأقل سيجعلك تعمل و الضيق يملأ صدرك، و هو ما سيؤدي إلي قلة القدرة علي الابتكار بشكلٍ فادحٍ سيؤثر بطبيعة الحال علي عملك كله.

لذا فقبل أن تدخل في تلك الدوائر الجهمنية من الإحباط و الضيق و ضياع الوقت و التشتت الذهني: تأكد قبل إقرارك لزعم أنك قد حللتَ علةً ما مِن أنك قد غطيت كافة الاحتمالات التي قد تظهر فيها تلك العلة. و هكذا يمكنك أن تستمر في مسيرة عملك بذات الفكر اليقظ و النفسية المستقرة.

السبت، 24 نوفمبر 2012

إنه يعمل، و لكنه لا يعمل !


حتى حينما حاولتُ جعل windows7 أفضل قليلاً و يُشبِه الـKDE فى إمكانيةٍ واحدةٍ لم يُجْد ذلك نفعاً؛ هذه الصورة التقطتُها لقائمة SendTo بعد أن جعلتُ برنامج الفهرست 3.0 يدعم windows7 و جربتُه علي جهازٍ به ذلك النظام:





ستُلاحِظون أن في القائمة المُنبثِقة هناك مجلدٌ اسمه "D"، هذا المجلد كان من المفترَض أن داخله تفرعاتٌ أخري كما فى صورة الـXP :

  
لكنها غير موجودة في حالة windows7 لأن النظام يمنع هذه الإمكانية ! كما أن قائمة المُفضَّلة Favourites أيضاً لا يستطيع الفهرست أن يغيرها كما يفعل مع الـXP. يعني أن الفهرست يؤدي عمله بشكلٍ صحيح و لا يمنعه نظام التشغيل من ذلك، و لكن نظام التشغيل يعود ليمنع نتائج هذا الفعل عن طريق تغيير القواعد التي كان يعتمد عليها الفهرست في عمله !
 بالطبع ليست هذه مشكلةً بالنسبة لي لأني لم أعد أستخدم أي نظام تشغيلٍ من microsoft إطلاقاً، و بالتالي لا يهمني هل ستعمل برامجي علي الأنظمة الجديدة منها أم لا. و أيضاً ليست هذه مشكلةً بالنسبة لغيري؛ فليس برنامج الفهرست من البرمجيات الضخمة التي يحتاجها الناس في حياتهم اليومية ولا يستطيعون الاستغناء عنها، بل كان بنسبةٍ كبيرةٍ مجرد مشروعٍ لتعلم لغة الـ#C و بيئة الـvisual studio. 

لكن المشكلة هي: ماذا سيحدث للبرامج الضخمة التي تعتمد علي كثيرٍ من المواصفات في الأنظمة القديمة من إنتاج microsoft حينما يتم تغيير تلك المواصفات في الأنظمة الأحدث بدون تقديم بديلٍ عنها ؟! بصراحة: لا أحب أن أضع نفسي محل من يُبرمِج حصرياً و خصيصاً لمنصات سلسلة الـ windows حينما يواجِه ذلك الموقف.

الخميس، 15 نوفمبر 2012

خدمة النسخ الاحتياطي الذكي

 خدمة النسخ الاحتياطي الذكي

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


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

الثلاثاء، 23 أكتوبر 2012

نظرية (المُبرمِج المُحتال)! جـ1

نظرية (المُبرمِج المُحتال)! جـ1

المُبرمِجون المُخضرمون يعلمون أن للكسل فوائداً عديدةً في المجال البرمجي، لدرجة أنه نُسِبَ لـ(بِل قيتس bill gates) أنه قال أنه حينما يُريد أن يُوكِل مهمةً ما إلي أحدٍ فإنه يُوكِلُها إلي شخصٍ كسولٍ لأنه سيختار أسهل الطرق لفعلها !
و قديماً شاهدتُ محاضرةً عن فوائد الكسل عند المُبرمِجين، حيث أنه يدفعهم لكتابةِ كودٍ سهل غير مُجهِدٍ لهم و عملِ واجهة برمجة API سهلة و آمنة، و غيرها من الأمور الأخري.
و كذا فقد وَصفَ لينوس تورفالدز linus torvalds نفسَه بأنه يُحِب أن يستريح و يشرب مشروبه المُفضل بينما ينهمك الآخرون في العمل (بعد أن يقوم باجتذابهم للعمل معه بمهارةٍ حقيقية و أساليبٍ تستحق التدريس).

حسنٌ: اليوم أريد أن أجعلكم تُلقون بهذه النظرية وراء ظهوركم و تنتبهوا لنظريةٍ أهم و أجمل منها و أكثر تفسيراً لما يجب أن يفعله المُبرمِجون كلهم بدون استثناء، 
أيها السادة إنها نظرية (المُبرمِج المُحتال) ^_^


تقوم هذه النظرية علي الأسس و التشابهات التالية بين المُبرمِجين و الأفَّاقِين:
  • المُحتال يتحايل علي الناس لجعلهم يُنفذون ما يُريد منهم (في الأغلب لسلبهم أموالهم) بحيلٍ متنوعة، و بينما يَظنون أنهم يفعلون أموراً غير مترابطة و لا يُمكن لذلك الأفَّاق أن يؤذيهم بها أو يسطو علي جهودهم فإن كل النتائج تترابط في النهاية لتُعطِي المُحتال ناتجاً نهائياً يرغب هو فيه بقوة.
    و المُبرمِج كذلك يتحايل علي المُعالِج و نظام التشغيل (و ربما المُستخدِم) لجعلهم يُنفذون ما يُريد، بينما هم لا يرون إلا خطواتٍ غير مترابطةٍ أو متناسقة.

  • المُحتال يهمه كثيراً أن يكون مظهر خدعته و مظهره شخصياً جذاباً، ليكسب ثقة الناس فيه و يحعلهم يتعاملون معه بأريحية و بساطة. و كذلك المُبرمِج يهمه أن يكون شكل واجهة برنامجه جميلاً حتي يتحمس الناس لاستخدامه أو علي الأقل لا يصدهم عن ذلك، و لا يلتفتون إلي المشاكل التي قد تظهر في بعض الأحيان أثناء عمل البرنامج؛ ففي تلك الحالة سيقول الواحد منهم: "هذا شيءٌ بسيط، دعنا لا نبخس هذا البرنامج الجميل حقه لوقوع خطأٍ لا أظن البرامج الأخري تتلافاه".

  • المُحتال ليس من النوعية المُزعِجة جداً بحيث يظل يتكلم و يتكلم حتي ينفلق دماغ من يجلس بجانبه، بل هو شخصٌ لبقٌ و قليل الكلام و لا يَنطق إلا بما فيه المصلحة و بالقدر الذي يحتاج إليه فقط.
    و المُبرمِج أيضاً يهمه أن يكتب أكواداً في غاية الإختصار بحيث لا يكون هناك أكوادٌ لا داعي لها و يُمكِن الإستغناء عنها. و بينما يُسمِّي الأفَّاق هذا "لباقة" يُطلِق المُبرمِج عليه صفتي "مقروئية readability" و "مكتوبية writability"، و لكن لا تنخدعوا باختلاف الألفاظ؛ فهذا لا يعني بالضرورة اختلاف المعاني :)
  • المُحتال لا يملأ عينه إلا التراب (بدرجةٍ أكبر من بقية البشر)؛ فهو طَمَّاعٌ أبداً و دائماً، و كلما حاز شيئاً ما زَهِدَه و رغب في غيره مما هو أحلي و/أو أغلي، و المُبرمِج طَمَّاعٌ أكثر من الطمع نفسه؛ فكلما طَوَّر برنامجه نظر إلي مُستوَيً جديدٍ يرغب في إيصال ذلك البرنامج إليه.
  • المُحتال لا يَصلح لتحمل المسئولية لأنه يعتبر الدنيا كلها غنيمةً ينبغي نهبها، و المُبرمِج لا يصلح لتولي المسئولية لأنه يري الدنيا كلها شاشة حاسوب و كوب قهوة (أو شاي) ينبغي التفرغ لهما تماماً.

هل رأيتم ؟


يُتبَع

السبت، 20 أكتوبر 2012

مكتبة الـOpenCV قمة الذكاء قمة الخيبة !

س: ما هي قمة الذكاء ؟
جـ: أن تُبرمِج مكتبةً قويةً جداً مثل مكتبة الـOpenCV، فى مجالٍ شديد التعقيد كمُعالَجة الصور الرقمية، ثم تَنشرَها للعالم كله مجاناً و تجعلها مفتوحة المصدر أيضاً.
س: و ما هي قمة الخيبة ؟
جـ: أن تكتب الكود بِلُغتَي الـC و الـ++C من غير سَطْر تعليقاتٍ واحدٍ يُوحِّد الله، و أن تترك الناس يُحاوِلون فهم أكوادك كلٌ حسب جهده و قدرته !

الأربعاء، 29 أغسطس 2012

المقدمة المُختصرة للبرمجة الكائنية oop كملف pdf

قمتُ بفضل الله تعالي بتحويل مقال المقدمة المُختصرة للبرمجة الكائنية oop إلي ملف pdf و رفعتُه علي حسابي علي موقع 4shared لإتاحة تحميله و تداوله و قراءته لمن يرغب في ذلك.

يمكنكم تحميل الملف من هــــنــــا.

تعقيبٌ علي مقال: بين الـawt و الـswing

تعقيبٌ علي مقال: بين الـawt و الـswing

عَلَّق أخٌ فاضلٌ علي مقالي بين الـawt و الـswing علي موقع وادي التقنية بقوله (بِتَصَرُّف):
ياه، أنت ما زلت تستخدم awt ! ظننتُ لا أحد يستخدمها الآن، ما رأيك في أن تستخدم مكتبة SWT؛ على الأقل هي أسرع من swing ومظهرها أفضل بدرجات، وهي في تطور مستمر.
بالمناسبة: هل السرعة مطلوبةٌ في التطبيق الذي تُطوره؛ لأنه من المعروف أن جافا في تطبيقات سطح المكتب بطيئةٌ بشكلٍ عام ؟ وكذلك من المعروف أن قوة جافا تظهر في المُخَدِّمات و الأجهزة المحمولة.
فقلتُ:

أنا لم أتعمق من قبل في دراسة الجافا و العمل الاحترافي بها، و كنتُ أستخدمُ الـ#C و الـ.NET في كل شيءٍ يخصني. و لذلك نسيتُ مسألة قِدَم الـawt و المشاكل التي تظهر حين استخدامها، و تذكرتُ هذا بعد ما حدث :)
بالنسة لسرعة التطبيق فلم تتأثر ببطء الواجهة الرسومية بشكلٍ كبير؛ لأنها لا تستخدمها إلا في الدخل و الخرج، و يمكن للبرنامج أن يستخدم سطر الأوامر العادي لأداء هذه المهمة إذا رغب مُستخدمه في ذلك، و مُحاكي سطر الأوامر الخاص له بعض الحالات الخاصة التي تجعله الأفضل (كما أنني بصراحة كنتُ أريد تعلم كيفية صنع مُحاكيات سطر الاوامر فقلتُ: وافق شِنٌ طبقه ^_^)

الثلاثاء، 28 أغسطس 2012

البرمجة الكائنية بدون وراثةٍ متعددة

البرمجة الكائنية oop بدون وراثةٍ متعددة multiple inheritance تماماً كزوجةٍ مُتدينةٍ جميلةٍ و لكنها لا تُنجب؛ بالتأكيد وجودها أفضل بكثيرٍ جداً من عدم وجودها، و لكن لا يُمكنك أن تُنكر أنه لا أحد يرغب في البقاء بلا أبناء.

السبت، 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
و غيرهن.