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

السلام عليكم

احد مباديء البرمجة بطريقة صحيحة هو مبدأ الفتح والإغلاق 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 حيث أن لكل واحدة طريقة مختلفة أو مكتبة مختلفة للإتصال.

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

 

 

Advertisements

اترك رد

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

WordPress.com Logo

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   / تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   / تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   / تغيير )

Google+ photo

أنت تعلق بإستخدام حساب Google+. تسجيل خروج   / تغيير )

Connecting to %s