إصدار كتاب: نحو مبادئ برمجية صحيحة

السلام عليكم ورحمة اللهSmallCover

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

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

Advertisements

مبدأ الفتح واﻹغلاق في البرمجة

السلام عليكم

احد مباديء البرمجة بطريقة صحيحة هو مبدأ الفتح والإغلاق Open closed principle. وهو يعني سماحية توسيع إمكانات الجزئية البرمجية واﻹضافة لها واستخدامها بطريقة مختلفة دون التعديل فيها.

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

الهدف من هذه الطريقة هي إعادة الاستخدام، لكن دون المساس بالكود الأساسي كما ذكرنا. وعدم المساس بالكود اﻷساسي يحقق لنا استقرار البرنامج بحيث لا تتأثر اﻷجزاء التي تستخدم هذه المكتبة أو البرامج الأخرى التي تسخدم نفس المكتبة، ويحقق لنا توسعة قاعدة الاستخدام للكود بين البرامج. كذلك الهدف هو إمكانية التعديل واﻹضافة بطريقة سهلة لإستيعاب المطلوبات الجديدة.

ما يحقق هذا المبدأ هي عدة ميزات لابد أن تتوفر في لغة البرمجة التي نستخدمها وهي: البرمجة الكائنية، والوراثة، ووتعدد اﻷشكال. كذلك فإننا نستخدم التجريد لتحقيق هذا الهدف. ونلاحظ أننا اول مرة يتطلب منا المبدأ أن تكون لغة البرمجة تدعم البرمجة الكائنية. حيث أن المباديء السابقة التي ذكرناها في تدوينات سابقة يمكن تحقيقها في لغات هيكلية structured لكن هذه المرة البرمجة الهيكلية لا تسمح لنا بتحقيق مبدأ الفتح واﻹغلاق.

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


CheckUser(String username, String password)

ChangeUserPassword(String username, String oldPassword, String newPassword)

InsertNewUser(String username, String password)

نحن نريد استخدام هذه الفئة مع أي قاعدة بيانات لكن تختلف كل قاعدة بيانات حسب تصميمها، مثلاً أحد المبرمجين يسمي الجدول المحتوي على أسماء المستخدمين users وآخر يسميه logins. كذلك فإن الحقول التي تحتويها هذه الجداول تختلف أسمائها، كذلك فإن كلمة المرور تختلف طريقة تخزينها، فمنهم من يستخرج منها ما يُسمى بالـ hash مثل MD5 ومنهم من يضع كلمة المرور كما هي في قاعدة البيانات. لذلك يكون من الصعب أن يكون لدينا نفس الكود أو نفس المكتبة التي يمكن استخدامها مع جميع هذه الجداول المختلفة التصميم.

لحل هذه المشكلة أولاً لابد من تجريد اﻹجراءات الداخلية والتي تتعامل مع الجداول مباشرة، مثلاً لابد من استخلاص هذا الجزء من الكود في إجراء منفصل:


query.Execute('select username, password from users where username = ?');

query.setParameter(1) = username;

مثلاً نُسميه getAuthenticationFromTable لكن المشكلة إذا كتبنا فيه أي كود فإننا نكون قد ربطناه بتصميم معين، وهو أن يكون اسم الجدول users وحقل اسم المستخدم username وكلمة المرور password فإذا اختلف أي من هذه التفاصيل فإن هذا الكود سوف لن يعمل.

نقوم ببساطة عدم كتابة متن هذا الإجراء في المكون، فقط نكتب اسمه والمدخلات والمخرجات أي نكتب رأس الإجراء header/interface ونحوله إلى إجراء إفتراضي virtual أي لا يوجد له كود في هذه المكتبة أو الفئة أو الوحدة، لكن على من يرث هذه المكتبة أن يقوم بكتابة كود هذا اﻹجراء بنفسه، فمثلاً هذا تعريف لهذا اﻹجراء في فئة الكائن اﻷساسي باستخدام لغة جافا:


public abstract ResultSet getAuthenticationFromTable(String username);

نلاحظ أنه لا يوجد كود بداخله، لكن مع ذلك يمكن نداءه من باقي إجراءات الفئة، مثلاً نقوم بمناداته داخل اﻹجراء checkUser:


public boolean checkUser(String username, String password) throws SQLException{

ResultSet result = getAuthenticationFromTable(username);
if (result.next()){
if (password.equals(result.getString(2))){
return true;
}
else {
return false;
}
}
else {
return false;
}
}

في هذه الحال لايمكن استخدام هذه الفئة مباشرة، إنما لابد من وراثتها ثم كتابة الإجراءات اﻹفتراضية فيها، مثلاً في برنامج للمبيعات نقوم بوراثتها في فئة جديدة نسميها PurchaseAuthentication وهذا هو الكود الذي تحتويه:


public class PurchaseAuthentication extends UserAuthenticationClass{

@Override
protected ResultSet getAuthenticationFromTable(String username){

// Do Database connection..
PreparedStatement statement = connection.prepareStatement("select username, password from users where username = ?");
statement.setString(1, username);
return statement.executeQuery();

}

}

عبارة @Override تعني أن هذا اﻹجراء سوف يقوم بإستبدال الإجراء الموروث، فكأنها وراثة عكسية، فبدلاً من أن ترث الفئة اﻹبن الفئة اﻷب، فإن اﻷب في هذه الحالة يرث اﻹبن ويستخدم إجراءه getAuthenticationFromTable

نلاحظ كذلك أننا فقط كتبنا إجراء واحد لكن في المقابل اعدنا استخدام كل اﻷجراءات الموجودة في الفئة اﻷصلية UserAuthenticationClass. عند استخدام الفئة نقوم بتعريفها ومناداتها كما في المثال التالي:


UserAuthenticationClass auth = new PurchaseAuthentication();
boolean success = auth.checkUser("mohammed", "mypassword");

نلاحظ أننا عرفنا الكائن على أنه من الفئة UserAuthenticationClass لكن عندما قمنا بعمل تهيئة له instantiation هيأناه على أنه من نوع  PurchaseAuthentication عندها يمكننا نداء أي إجراء من الفئة  UserAuthenticationClass. بهذه الطريقة يمكننا استخدام الفئة UserAuthenticationClass مع أي برنامج مهما كان شكل قاعدة بياناتها وجداولها دون أن نقوم بعمل أي تغيير في الفئة الرئيسة UserAuthenticationClass.

يمكننا كذلك تجريد اﻹتصال مع قاعدة البيانات ونجعلها افتراضية للسماح بإمكانية اﻹتصال مع أي نوع من محركات قواعد البيانات مثلا أوراكل، MySQL أو FireBird حيث أن لكل واحدة طريقة مختلفة أو مكتبة مختلفة للإتصال.

بهذه الطريقة نكون قد وسعنا إعادة استخدام الكود بالنسبة للفئات المجردة، أو نقوم بتجريد الفئات ثم نستخدمها في عدد من البرامج، أي يمكننا كتابة مكتبة بها كل هذه الفئات المجردة ثم نقوم باستخدام هذه المكتبة في باقي البرامج بدلاً من إعادة كتابة نفس الكود عدة مرات مع كل برنامج جديد مع اختلاف بسيط من برنامج لآخر. ويمكن للمبرمجين اﻷكثرخبرة أن يقومون بكتابة هذه المكتبات لتقليل اﻷخطاء التي يمكن أن يقع فيها المبرمجين المبتدئون.

 

 

خمسة أعوام ونصف لاستخدام نظام أوبونتو لينكس والبرامج المفتوحة المصدر

السلام عليكم ورحمة الله، وكل عام وانتم بخير

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

بدأت بدراسة نظام لينكس عندما كُنت لا أزال في الجامعة، في عام 1997، وكان ذلك باستخدام معمل يحتوي على أجهزة طرفية بسيطة dummy terminals بعمنى الكلمة، أي لا يوجد فيها معالج، حيث أنها تستخدم معالج وذاكرة الجهاز رئيسي واحد ويتم توصيله بها وعن طريق  كيبل   يشبه الكيبل المتوازي  parallel cable. وكانت الواجهة فقط نصية terminal فكان كل الاستخدام هو للأوامر المسماة بالـ command line.

بعد ذلك لم يتسنى لي استخدام نظام لينكس، حيث لم تتوفر أي وسيلة لتثبيته مع وندوز – حينها كان نظام وندوز 95 – وكانت اﻷقراص الصلبة قليلة السعة، تكفي فقط لنظام تشغيل واحد، أذكر أن القرص الصلب لحاسوبي المنزلي كان 1 قيقا بايت فقط . طبعاً كانت سعة كبيرة حينها. بعد أن تخرجت من الجامعة وعملت في شركة اتصالات اصبحت أقراص نظام لينكس أكثر توفراً، كذلك فقد حضرت دروس لنظام ريدهات لينكس، وكان حينها النسخة رقم 7.1 أو 7.2 على ما أذكر.

بعد ذلك ظهر نظام فيدورا لينكس فقمت بتثبيته فى حاسوبي المنزلي على ما أذكر مع نظام وندوز، ثم استبدلته بعض عدة أعوام بنظام ماندريك الذي تغير اسمه إلى ماندريفا بعد عدة أعوام، ثم اخيراً قمت بتجربة أوبونتو لينكس والذي مازلت استخدمه إلى اﻵن.

في بداية عام 2012 تركت الوظيفة وقمت بتأسيس عملي الخاص – فأصحبت أكثر حرية في اختيار التقنيات- وكان أول تغيير هو الإعتماد على المصادر المفتوحة فقط في الإستخدام ولتطوير البرامج، فقمت بتثبيت نظام أوبونتو كنظام أساسي لكن جنباً إلى جنب مع نظام وندوز الذي كنت احتاج إليه في بعض الحالات، ثم بعد ذلك اصبح نظام لينكس هو النظام اﻷساسي في الأجهزة التي استخدمها ونظام وندوز قمت بتثبيته داخله باستخدام برنامج Virtual Box واصبحت لا افتح نظام وندوز إلا نادراً. والأسباب التي تجعلني استخدم وندوز أحياناً هي:

  1. تجربة البرامج التي أنتجها في نظام لينكس للتأكد أنها تعمل بطريقة سليمة في نظام وندوز
  2. إعادة ترجمة برامج فري باسكال لإنتاج نسخة وندوز من تلك البرامج (مثل برنامج تسجيل المحاسبي)
  3. استخدام البرامج التي لا تعمل إلا في وندوز مثل MS-SQL Enterprise
  4. تشغيل بعض البرامج التي بها مشكلة في نظام لينكس، مثل برامج عميل Cisco VPN وفي السابق Juniper VPN لكن الحمد لله وجدت حل للأخير في نظام أوبونتو واصبح يعمل بدون مشاكل

باستخدام البرامج المفتوحة المصدر فقد قُمت بتقليل التكلفة بصورة كبيرة جداً، فمثلاً نظام التشغيل مجاني لكل جهاز كمبيوتر أقوم بشراءه لنفسي أو لأحد الموظفين أو المتعاقدين معنا في العمل، وحتى لأبنائي. كذلك لا نحتاج لشراء برامج، مثل أدوات التطوير البرمجية، حيث نستخدم برامج مفتوحة المصدر مثل NetBeans وقد اشتريت نُسخة وندوز 7 مرة واحدة فقط واصبحت أنقلها داخل نظام لينكس مع كل لابتوب جديد دون أن يشعر نظام الوندوز أن هناك تغيير في العتاد.

كذلك تم تقليل تجهيز المخدمات لدي الزبائن من ناحية نظام التشغيل، حيث بلغت المخدمات التي تعمل فيها برامجنا والتي نشرف عليها مباشرة حوالي 40 مخدماً، كذلك قلت تكلفة الترقية، حيث نستخدم مخدم أوبونتو ويمكننا ترقيته إلى أي نسخة جديدة بدون أي تكلفة تذكر.

كل البرامج المكتبية المساعدة تأتي مجانية مع نظام لينكس مثل LibreOffice و GIMP لتحرير الصور وغيرها ولم نحتاج إلى أي برامج أخرى إضافية، كل البرامج التي نحتاج إليها كمطوري برامج نجدها موجودة في مستودعات أوبونتو أو في اﻹنترنت.

تتطلب عرض الشرائح هذه للجافا سكريبت.

نستخدم كذلك لغات البرمجة المجانية ومفتوحة المصدر مثل لغة جافا، PHP، ولغة Golang وبالنسبة لقواعد البيانات نستخدم MySQL وFirebird .و في جانب مخدمات الويب نستخدم apache و tomcat.

تمتاز البرامج المفتوحة المصدر بالبساطة مما جعلها لا تستهلك قدراً كبيراً من موارد العتاد، حيث كُنت استخدم لابتوب Core I5 طوال الأعوام السابقة، لكن استبدلته بجهاز Core I3 ولم الحظ أنه بطيء  بصورة كبيرة، حيث أنه يكفي تماماً العمل الذي أقوم به من تطوير برامج ، وتصفح للإنترنت و كتابة كتب ووثائق وغيرها من النشاطات اﻷخرى.

اشتريت قبل عامين قرص صلب من نوع SSD وقمت بتثبيت نظام أوبونتو فيه في لابتوب من نوع توشيبا، وبعدما اشتريت لابتوب جديد من نوع Dell قمت فقط بنقل القرص القديم من لابتوب توشيبا إلى اللابتوب الجديد، وقد عمل نظام أوبونتو مباشرة ولم تحدث أي مشكلة ولم يحتاج إلى إعاد تعريف لأي من العتاد. فقط طلب مني إعادة إدخال كلمات المرور للشبكات الوايفاي.

حتى الألعاب والبرامج التعليمية بالنسبة للأطفال اصبحنا نستخدمها في نظام أبونتو حيث يوجد عدد كبير منها وتتميز بالبساطة وأنها أقل عنفاً من نظيراتها ألعاب وندوز والـ playstation.

بالنسبة لتخصص شبكات الكمبيوتر فإن نظام لينكس يحتوي على عدد كبير من البرامج الخدمية والـ command line tools التي تُساعد في تقصي مشاكل الشبكات أو البرامج مثل tcpdump, telnet, nc وغيرها من البرامج التي تأتي مباشرة مع نظام لينكس.

رأي أن أنقل تجربتي وأن أعترف بما قدمته لنا المصادر المفتوحة ولزبائننا بعدما اصبح العمل مستقراً – بفضل الله- واصبحت التجربة تستحق الذكر.

 

 

التسميات في البرمجة

السلام عليكم

احد المواضيع المهمة ضمن المباديء الصحيحة للبرمجة هو كتابة كود مقروء وسهل الصيانة، ولتحقيق ذلك لابد من استخدام عدة تقنيات منها الطريقة القياسية والصحيحة لتسمية المتغيرات،  واﻹجراءات، و الكائنات، والوحدات وحتى البرامج وأجزاءها المختلفة.

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

تسمية اﻹجراءات على سبيل المثال والكائنات لابد أن يكون شاملاً ومختصراً ويعبر عن الوحدة البرمجية المستهدفة بطريقة مباشرة لوظيفتها. مثلاً إذا كان المراد كتابة دالة لقراءة معلومة من ملف إعدادات يمكن أن يكون إسمها كأحد الخيارات التالية:

readConfigValue, getConfigValue, readConfigParameter, getConfigurationParameter

نلاحظ أنها واضحة وتتكون من ثلاث مقاطع، المقطع اﻷول read يعني أن هذه الدالة تقوم بالقراءة، والمقطع الثاني: Config يشرح الهدف الذي نريد قراءته، والمقطع الثالث Value يحدد بصورة أكثر دقة الناتج من الدالة  وهو قراءة القيمة لهذا الباراميتر.

أما إذا استخدمنا احد اﻷسماء التالية:

configValue, readX, getValue

نجد أن الإسم اﻷول: configValue ليس فيه الفعل الذي نريد تنفيذه وهو القراءة، لذلك يجد من يريد صيانة الكود أو التطوير فيه، يجد صعوبة فهم الغاية من هذه الدالة، واﻹسم الثاني readX لا يوضح ماذا نريد أن نقرأ، والإسم الثالث: getValue أيضاً لا يوضح أننا نريد القراءة من ملف اﻹعدادات.

كذلك فإن الشمولية في اﻹسم مهمة جداً، ويُفضل أن تكون مجردة قدر اﻹمكان، فإذا كانت الفئة أو الوحدة مثلاً المراد منها القراءة والكتابة من قاعدة البيانات فلا يصح أن نقوم بتسميتها DataReader فهي لا تشمل الكتابة  فالأفضل تسميتها DataReaderAndWriter أو الإكتفاء بالتسمية المجرة وهي DataClass أو DataManipulation أو Database. والتسمية المجردة لها فائدة في التطوير المستقبلي، مثلاً إذا كانت الدوال داخل تلك الوحدة البرمجية هي فقط دوال للقراءة فإن تسميتها DataReader يكون صحيحاً ما لم تتم إضافة دالة للكتابة، أما إذا تمت إضافة وظيفة الكتابة في قاعدة البيانات فلابد من تغيير اﻹسم، لكن إذا نسى المبرمج ذلك فإذن ذلك يقود إلى أن تصبح اﻷسماء غير مطابقة للوظائف ويبدأ البرنامج بالحيد من إتباعه للمباديء الصحيحة للبرمجة.

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

تعديل اﻷسماء أحياناً يكون سهل وذلك في اﻷجزاء الداخلية للبرامج، فيتم تعديل اﻷسماء لعدة حالات نذكر منها: الحالة اﻷولى إذا لم تكن دلالاتها صحيحة، في هذه الحال تكون ضمن عملية إعادة صياغة الكود، وذلك لتصحيح الهدف من تلك الوحدة البرمجية. والسبب الثاني هو توسعة الوظيفة، وكمثال ما ذكرناه سابقاً في الوحدة التي كان اسمها DataReader قمنا بتجريد إسمها إلى DataClass لإضافة وظيفة الكتابة مع القراءة في قاعدة البيانات. السبب الثالث هو التجريد لإعادة اﻹستخدام في برامج أخرى أو أجزاء أخرى. مثلاً إجراء للربط مع قاعدة البيانات المحاسبة كان اسمه AccountingDBConnection نقوم بتسميته إلى DatabaseConnection لإمكانية استخدامه مع أي برنامج وأي نظام آخر دون تحديد اسمه.

تعديل اﻷسماء يكون صعباً في حال أنه واجهة برمجية interface مثل إسم خدمة ويب أو إجراء ضمنها أو حتى باراميتر وذلك لأنه إتفاق يجب عدم اﻹخلال به مع برامج أخرى تستخدم هذه اﻷسماء لتنفيذ تلك الدوال فإذا تم التغيير في هذه اﻷسماء فإن البرنامج المستفيدة من هذه الخدمات سوف تتوقف. كذلك عند كتابة مكتبة يتم استخدامها في أكثر من نظام أو منشورة في النت، فإن تغيير الدوال يجعل البرامج المعتمدة عليها ينتج عنها خطأ في حالة إعادة ترجمتها.

 من المباديء واﻷهداف للتي نحققها باستخدام طريقة صحيحة للتسمية هي:

  • سهولة قراءة وصيانة الكود
  • تحقيق التجريد
  • تحقيق وتسهيل إعادة اﻹستخدام
  • سهولة توقع وظيفة أي جزء من الكود
  • معرفة نقاط الضعف في أجزاء معينة من الكود مثلاً اﻷرتباط

مبدأ الوظيفة الواحدة في البرمجة

السلام عليكم

هذه مقالة أخرى من سلسلة المباديء الصحيحة للبرمجة والتي تقع تحت دائرة هندسة البرمجيات. وتتكلم عن مبدأ الوظيفة أو المسؤولية الواحدة لوحدة الكود single responsibility principle.

الفكرة ببساطة هي أن يكون للوحدة من الكود هدف واحد من كتابتها، مثلاً أن تكون وحدة متخصصة في قراءة البيانات من قاعدة البيانات العلائقية، مثلاً نسميها access unit وأخرى متخصصة في واجهة المستخدم فنسميها presentation unit او class أو حتى Layer حسب التقسيم المتبع وحجم الوحدة المتخصصة. بهذا نكون قد جعلنا لكل وحدة سبباً واحداً للتغيير، مثلاً إذا كانت المهمة تغيير في واجهة المستخدم لايؤثر ذلك على طريقة قراءة المعلومات من قاعدة البيانات، وهذا يجعل الكود أسهل تعديلاً.

نأخذ مثال لإجراء به أكثر من وظيفة ثم نقوم بتصحيحه. والمثال مكتوب بلغة برمجة إفتراضية وليست من اللغات المعروفة، مهمته قراءة بيانات من جدول في قاعدة بيانات ثم عرضها في المتصفح:

function ReadAndDisplayTable
{
  connection = Connection("localhost", "MyData", "user", "password")

  dataset = connection.query("select * from students")

  html.print("<table><tr><th>ID</th><th>Name</th></tr>")
  for record in dataset
  {
    html.print("<tr>")
    html.print("<td>", record["id"], "</td>")

    html.print("<td>", record["name"], "</td>")
    html.print("</tr></table>")
  }
  connection.close

}

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

التصحيح هو أن نقوم بفصلهما  في إجرائين منفصلين، واﻷفضل أن تكون وحدات مختلفة، مثلاً كل واحد في مكتبة منفصلة أو class منفصلة تجمع نوع متشابه من الوظائف:

function ReadTable: Dataset
{
  connection = Connection("localhost", "MyData", "user", "password")

  dataset = connection.query("select * from students")
  connection.close
  retrn dataset
}

function displayTable(dataset: Dataset)
{

  html.print("<table><tr><th>ID</th><th>Name</th></tr>")

  for record in dataset
  {
    html.print("<tr>")
    html.print("<td>",record["id"],"</td>")
    html.print("<td>",record["name"],"</td>")
    html.print("</tr></table>")
  }
}

بهذه الطريقة يكون لدينا إجراء لقراءة البيانات فقط (readTable) وآخر لكتابته (displayTable) ونحقق أيضاً إعادة استخدام الكود، حيث يمكن استخدام اﻹجراء readTable في أي مكان آخر ليس له علاقة بإظهار البيانات في المتصفح، مثلاً لقرائتها ثم إرسالها بالبريد، كذلك يمكن نداء إجراء الكتابة في المتصفح بعد قراءة البيانات من ملف في القرص الصلب مثلاً.

هذه الطريقة تضمن كتابة كود واضح القراءة، سهل التعديل والصيانة، ومتعدد الإستخدام.

إضافة:
مبدأ الوظيفة الواحدة يُساهم في تسهيل اﻹختبار، حيث يمكن اختبار هذه الوحدة البرمجية بمعزل عن باقي الوحدات

الكتب كبديل للتلفاز

السلام عليكم ورحمة الله

مازلت إلى الآن – بفضل الله- متمسك بفكرة اﻹستغناء عن التلفاز من أجل أن يتربى أبنائي في بيئة مختلفة، بيئة علمية، وعملية ليس فيها أي إضاعة للوقت ، ليس فقط للأطفال، بل حتى لنا – نحن الكبار-  حيث أننا نحتاج إلى استغلال كل اﻷوقات في اﻷشياء المهمة فقط.

التلفاز يأخذ وقت كبير جداً من الناس منذ القدم، وعدم وجوده يترك فراغاً كبيراً لابد من ملائه بأشياء مفيدة، وليس شيئاً واحدا، حيث أن اﻷشياء المفيدة ربما يمل منها الناس، لذلك لابد من توفر أشياء متعددة يتنقل الشخص بينها للاستفادة من هذا الوقت الثمين.

تتطلب عرض الشرائح هذه للجافا سكريبت.

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

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

أدعو سكان الخرطوم ومن زارها بزيارة مكتبة الدار السودانية للكتب، وهي أكبر مكتبة في الشرق اﻷوسط وأفريقيا، والتي تكلمت عنها في تدوينة سابقة.

كل الأوقات مهمة

السلام عليكم ورحمة الله stopwatch

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

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

التقدم في العمر لا يعني أن تهتم بساعاتك فقط وماتقضيه بها، بل يتجاوز ذلك إلى من تعول من اﻷبناء، فما كنت ترضاه لهم في السابق من تضييع اﻷوقات لا ترضاه اﻵن وترى أن أوقاتهم هذه ثمينة دون أن يعووا هم أهميتها، فما تزال تنصحهم فينتصح بعضهم ويولي اﻵخر.

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

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

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

لا اريد أن أنصح الناس أن لا يضيعوا أوقاتهم في الفراغ وماليس به فائدة، هذه النصيحة تجاوزناها بفضل الله، اﻵن نحن في زمن السرعة وزيادة قيمة الوقت بصورة لا متناهية، فأنصح الناس أن لا يضيعوا أوقاتهم في اﻷعمال اﻷقل إنتاجاً، فلابد من زيادة التحدي بعد كل فترة لإنجاز أكبر في كل عام عن العام الذي سبقه، وإلا فنحن نقف في نفس النقطة لا نتحرك عنها.

لأهمية الوقت لا أريد تضييع أوقات القراء، فالموضوع ذو شجون، وأتمنى أن تكون الفكرة قد وصلت.