مصطلحات ستعيش معها في مجال تصميم البرمجيات Software Design

1

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

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

ماهو الارتباط Coupling
نتحدث عن الارتباط Coupling في سياق العلاقات بين الوحدات Modules، فحينما تكون عندنا كلاس تستعمل Object من كلاس أخرى فنقول بأن هاتين الفئتين مترابطتين، ويمكن لهذا الارتباط أن يكون ضعيفا Loose coupling أو قويا Tight coupling حسب درجة الارتباط بين الوحدات.
سأعطيك مثالا بسيطا، لنفترض أن عندنا كلاس اسمها EmployeeService وهي كلاس تحتوي على مجموعة من الوظائف بما فيها الوظيفة Add(Employee employee) كما يلي:

EmployeeService.cs:

    public class EmployeeService
    {
        public void Add(Employee employee)
        {
            // Save to Database for example.
        }
    }

ثم عندنا كلاس آخر يستعمل الكلاس EmployeeService بغرض إضافة موظف جديد كما يلي:

TestEmployee.cs:

    public class TestEmployee
    {
        readonly EmployeeService service = new EmployeeService();

        public void Save()
        {
            service.Add(new Employee());
        }
    }

الكلاس TestEmployee يقوم بإنشاء Instance من النوع EmployeeService، أي صار بينهما ارتباط، هذا الارتباط هو ارتباط قوي Tight Coupling لأن الكلاس TestEmployee مرتبطة بتفاصيل الكلاس EmployeeService وبالتالي أي تغيير يحدث على مستوى هذا الكلاس الأخير سيجرنا إلى التعديل على الكلاس TestEmployee، وهذه إحدى سلبيات الارتباط القوي.
وحتى نفهم الأمر أكثر سنعطي مثالا آخر يبين علاقة الارتباط بين الفئات الثلاثة Book و BookRepository و BookController بحيث:
الفئة Book عبارة عن موديل يحتوي على الخصائص الخاصة بالكتاب مثل العنوان، عدد الصفحات، تاريخ النشر.
الفئة BookRepository: عبارة عن كلاس يقوم بجلب البيانات من مصدر معين وليكن مثلا قاعدة بيانات أو RESTful service.
الفئة BookController: عبارة عن كلاس يقوم باستهلاك BookRepository بغرض الاستفادة من الوظائف التي يقدمها.
ولنبدأ في الأول مع الكود الخاص بالفئة Book:

Book.cs:

    public class Book
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public int NumberOfPages { get; set; }
        public DateTime PublishedAt { get; set; }
    }

وهنا الكود الخاص بالكلاس BookRepository التي تجلب البيانات الخاصة بالكتب من مصدر معين، تم اختصار الكود لدواعي الشرح وتم الاقتصار على ما يعنينا:

BookRepository.cs:

    public class BookRepository
    {
        public IEnumerable<Book> GetAllBooks()
        {
            // GET DATA FROM A DATA SOURCE
        }
    }

بالنسبة للفئة التي ستستهلك هذه Repository فهي الفئة BookController، والتي سيكون الكود الخاص بها تقريبا كما يلي:



EmployeeService.cs:

    public class BookController
    {
        public BookRepository Repository { get; set; }

        public BookController(BookRepository repository)
        {
            Repository = repository;
        }

        public IEnumerable<Book> GetAllBooks()
        {
            return Repository.GetAllBooks();
        }
    }

الكلاس أعلاه يقوم بإنشاء Instance من BookRepository ثم يقوم بإعطائها قيمتها داخل المشيد Constructor، وبعد ذلك يقوم باستعمال هذه Instance من أجل إرجاع بيانات الكتب باستعمال الوظيفة GetAllBooks المعرفة على مستوى BookRepository.
هذه الكلاس باختصار لكي تؤدي عملها فهي تحتاج إلى نسخة من الكلاس BookRepository بمعنى أنها مرتبطة بها، وأن بين الفئتين BookController و BookRepository ارتباط قوي، بحيث لو أردنا في المستقبل أن نغير التفاصيل Details الخاصة بالكلاس BookRepository فإننا ملزمون كذلك بتغيير Details الخاص بالكلاس BookController، أي أن هذين الفئتين مرتبطتان ببعض بشكل كبير، وهو ما يعرف بالارتباط القوي Tight Coupling.
الارتباط القوي يؤثر في الغالب بشكل سلبي على جودة المشاريع لأن الكلاسات المرتبطة يكون لها تأثير مباشر على بعضها البعض، ولأن الارتباطات تجعل من عملية الاختبارات أمرا صعبا، خاصة الاختبارات الأحادية Unit Tests، ناهيك عن صعوبة صيانة الكود وقراءته والزيادة عليه في حال وجود ارتباطات كثيرة ومتشعبة.
قد لا يبدو لك الضرر جليا في هذا المثال الذي نستعمله لأنه مجرد مثال صغير، لكن في المشاريع الفعلية الكبيرة التي تحتاج إلى أن يكون الكود مفهوما Understandable، قابلا للاختبار Testable، قابلا للصيانة Maintainable، قابلا للتوسيع والزيادة Extensible ستكون الارتباطات القوية الكثيرة عائقا حقيقيا أمام هذه المتطلبات.
لذلك ينصح دائما أن يكون التصميم المعتمد في المشروع يسعى إلى تقليل الارتباطات أو إلى توهينها وإضعافها فيما يعرف بعملية Loose Coupling كأقل تقدير عبر تطبيق بعض نماذج التصميم Design Patterns مثل Dependency Injection وغيرها، لكن لأن الحال ما يزال مبكرا للحديث عن هذه الأمور فدعني أختصر عليك الكلام عن الارتباطات.
الارتباطات هي العلاقات بين وحدات المشروع، والتي كلما ازدادات وقويت كان المشروع عصيا على الصيانة والاختبار والتوسيع، وكلما قلت وضعفت تحققت هذه الأمور، وسنرى كيف نقوم بتوهين وإضعاف الارتباطات بين الكلاسات لاحقا إن شاء الله.

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

Employee.cs:

    public class Employee
    {
        public string Name { get; set; }
        public string Company { get; set; }
        public DateTime DateOfBirth { get; set; }
        public bool IsLocked { get; set; }

        public string GetEmployeeName() => this.Name;

        public int Age() => DateTime.Now.Year - DateOfBirth.Year;

        public bool EmployeeLoginState() => this.IsLocked;
    }

الكلاس أعلاه غير متماسك لأن عناصره غير مرتبطة فيما بينها، فالكلاس Employee لا ينبغي أن تشتمل على مكون خاص باسم الشركة Company، كما أن حالة المستخدم IsLocked يفترض بها أن تكون في كلاس آخر خاص بمعلومات المستخدم User، بالتالي الكلاس ليس كلاسا متماسكا نظرا لاشتماله على عناصر غير متماسكة بالنظر إلى المسؤولية الأساسية للكلاس، فهو كلاس مسؤول عن الموظف Employee وليس عن حالة تسجيل الدخول أو معلومات الشركة.
يمكننا التعديل على الكلاس أعلاه لنحصل على ثلاث فئات متماسكة كما يلي، علما أننا اختصرنا الكود الخاص بالفئات للتركيز على ما يهمنا:


Employee.cs:

    public class Employee
    {
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string GetEmployeeName() => this.Name;
        public int Age() => DateTime.Now.Year - DateOfBirth.Year;
}


Company.cs:

    public class Company
    {
        public string Name { get; set; }
        public string Address { get; set; }
    }


User.cs:

    public class User
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public bool IsLocked { get; set; }
    }

الآن صار عندنا ثلاث فئات متماسكة، كل واحدة تحتوي على عناصر مترابطة فيما بينها، وتعكس المسؤولية المنوطة بالكلاس، فالكلاس Employee خاصة بالموظف، والكلاس Company خاصة بالشركة، والكلاس User خاصة بالمستخدم، وبهذا نكون قد جعلنا تصميمنا أكثر تماسكا More cohesive، وهي سمة جيدة تعكس جودة المشروع.

ماهو التجريد Abstraction
لنفهم التجريد Abstraction علينا في الأول أن نفهم الواقعية Concretness، نظرا لقيام المفهوم الأول على المفهوم الأخير، وحتى لا أدخل بك في دوامات من الفلسفة سأعطيك مثالا بسيطا لتفهم الفرق بين الأمرين بشكل جلي وواضح.
حينما تفتح صفحتك على الفيسبوك وترى صديقك قد كتب منشورا على الشكل التالي:
لغة البرمجة صعبة جدا وتستلزم منك الصبر نظرا لتعقيداتها وصعوباتها.
من غير شك فإنك مثلي ستتساءل: عن أية لغة برمجة يتحدث صديقك؟
لأن لغة البرمجة هكذا: عبارة عن شيء مجرد Abstract، غير واقعي not concrete، لكن حينما يوضح في منشوره مثلا أنه يتحدث عن لغة سي شارب أو جافا أو بايتون، فهو الآن دخل في نطاق الواقعية Concreteness لأنه تحدث عن شيء بعينه، وهذا هو الفرق بين التجريد والواقعية.
سأزيدك من الشعر بيتا لنفهم الأمر بشكل جيد، حينما نقول: حيوان Animal فنحن نتحدث بشكل مجرد أي أن الحيوان ليس شيئا واقعيا هو مجرد تجريد Abstraction لنوع معين، لكن حينما نقول أسد Lion، أو نمر Tiger، أو كلب Dog فنحن هنا نتحدث عن نوع موجود وملموس Concrete.
نفس الشيء حينما أقول لك: بشر Human ففي الحقيقة هذا اصطلاح تجريدي ولا يوجد شيء محدد ملموس في الواقع يسمى بشر، الموجود فعليا هم أشخاص بمواصفات معينة ووظائف محددة، فالبشر عبارة عن Abstract type، والنجار مثلا أو سائق التاكسي عبارة عن Concrete type.
في البرمجة يسري نفس التعريف، فلو عندنا كلاس تحتوي على وظائفها وخصائصها الخاصة ويمكننا إنشاء نسخ Instances منها، فنحن نتحدث عن فئات واقعية Concrete Classes، وحينما يكون عندنا نوع مجرد لا يحتوي على أية تفاصيل، فقط يقوم بعرض بعض المعلومات العامة التي من الممكن أن تشترك فيها العديد من الأنواع فنحن هنا نتحدث عن التجريد Abstraction، وفي البرمجة العناصر التي تسمح لنا بتطبيق مفهوم التجريد هي الفئات المجردة Abstract classes  والواجهات Interfaces.

مامعنى التفاصيل Details
في الحقيقة يستخدم هذا المفهوم في مجال تصميم البرمجيات كنقيض لمفهوم التجريد Abstraction، حيث يتم استعماله للتدليل على الفئات الواقعية Concrete classes، ومنه يقول روبرت مارتن في شرح البند الثاني من المبدأ الخامس لمبادئ SOLID، والحديث هنا عن مبدأ Dependency Inversion Principle، يقول فيه:

Abstraction should not depend on details, details should depend on abstraction.

ويعني به أن الواجهات Interfaces أو الفئات المجردة Abstract classes لا ينبغي أن تكون مرتبطة بالفئات الواقعية Concrete classes بل العكس هو الذي ينبغي أن يكون.
وطبعا هذا الكلام يأتي في سياق آليات إضعاف الارتباط بين الفئات والذي سنراه في وقته بالتفصيل إن شاء الله.
الشاهد هنا، أن العم بوب (وهو نفسه السيد روبرت مارتن) جعل Details على الطرف النقيض لل Abstraction مما يعني أن التعريف الذي أعطيناه في الفقرة السابقة للفئات الواقعية Concrete classes يبقى ساريا أيضا على مفهوم التفاصيل Details، وبالتالي يمكننا تعريف هذه الأخيرة بأنها كل كلاس واقعي نستدعيه بشكل مباشر في كلاس آخر، حيث يصير هذا الكلاس الآخر مقيدا بتفاصيل الكلاس الأول.

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

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

مدخل إلى مبادئ ونماذج تصميم البرمجيات Software Design Principles and Patterns

0

مقدمة

تتأثر جودة البرمجيات بمستوى التصميم Design الذي تم اعتماده من قبل الفريق الذي يسهر على تطويرها، حيث يلعب تصميم البرمجيات دورا كبيرا في جعلها قابلة للصيانة والتحديث Maintainable، ومفتوحة على الزيادة والتوسيع Extensible، وقادرة على الخضوع للاختبارات Testable التي من شأنها التحقق من سلامة اشتغال مختلف أجزاء البرنامج وأدائها للمطلوب منها بالشكل المتوقع. لذلك ينبغي أن يولي كل مشتغل في مجال صناعة البرمجيات أهمية كبيرة لعملية التصميم نظرا لتأثيرها المباشر على جودة المنتوج المقدم.
ولا يمكن للتصميم أن يكون ناجحا إلا إذا كان يمنح المزايا الثلاثة التي تقدم ذكرها وهي كالآتي:
-القابلية للتحديث والصيانة: إذ يلزم أن يكون المشروع قابلا للتحديث في أي وقت دون أن يكون لهذه العملية تأثير سلبي على باقي أجزاء المشروع.
-القابلية للزيادة والتوسيع: كل مشروع لا يسمح بإضافة أجزاء جديدة عليه، أو توسيع أجزاء موجودة مسبقا، دون أن تتأثر باقي المكونات بشكل سلبي، فهو مشروع ناقص ومحدود.
-القابلية للاختبار: الاختبارات مهمة جدا لضمان سلامة اشتغال أجزاء المشروع، ولكي يكون المشروع قابلا للاختبار عليه أن يكون منضبطا لمجموعة من القواعد التابعة لمجال التصميم.
عملية تصميم البرمجيات ترتكز على مجموعة من المبادئ والنماذج Principles and Patterns التي وضعها أناس متضلعون في هذا المجال بغرض حل إشكاليات تعرضوا لها باستمرار، فقاموا بتقديم حلول جاهزة يمكن لأي مطور أن يستعملها في وضعيات مماثلة.
أغلب هذه الحلول - التي تأتي على شكل مبادئ ونماذج - متوافقة بشكل كبير مع نمط البرمجة كائنية التوجه Object Oriented Programming Paradigm، حيث تستعمل هذه المبادئ والنماذج مفاهيم البرمجة الكائنية مثل الوراثة Inheritance وتعدد الأشكال Polymorphism و التجريد Abstraction وغيرها من أجل صياغة حلول متكاملة ومنظمة.
الهدف من وراء هذه المقالة أن نعرف بمجال تصميم البرمجيات وماله من تأثير بالغ على نجاح البرمجيات، لأنني شخصيا عانيت من عواقب تغييب ممارسات هذا المجال، فاضطررت في مرات عديدة إلى إعادة بعض المشاريع من الصفر لأنني لم أحترم فيها مبادئ ونماذج التصميم، الشيء الذي جعل هذه المشاريع منغلقة على ذاتها غير قابلة للتوسيع أو الصيانة، إضافة إلى صعوبة العودة إلى الكود وتفحصه وتتبعه بغرض تجويده وتحسينه أو بغرض إصلاحه وتصويبه فيما يعرف بعملية Refactoring، ناهيك عن إمكانية كتابة اختبارات على هذه الأكواد وخصوصا الاختبارات الأحادية Unit Tests، فقد كان ذلك ضربا من الوهم لأن الكود الذي كنت أكتبه كان هو الوهم نفسه :). 
فتعلمت من الأخطاء السالفة إلزامية الإلمام بمجال التصميم في صناعة البرمجيات، وتكييف الوضعيات البرمجية مع مبادئ التصميم Design Principles، فأحببت أن أشارككم هذه المعلومات على هيئة مقالات. 

ماهي مبادئ التصميم Design Principles

مبادئ التصميم هي مجموعة من القواعد والتوجيهات التي توضح لنا كيف ينبغي أن يكون شكل وسلوك الكود الذي نقوم بكتابته، حيث تفرض علينا مجموعة من القيود التي بالانضباط لها سنكتب كودا نظيفا Clean Code، قابلا للصيانة Maintainable، قابلا للزيادة والتوسيع Extensible، وقابلا للاختبار Testable كالاختبارات الأحادية Unit Tests، والغرض من هذه المبادئ هو تمكين المطور من التخلص من العادات السلبية التي تدفعه إلى التركيز على الوصول إلى الحل فقط دون الأخذ بعين الاعتبار جودة هذا الحل، ومدى قدرة هذا الحل على التكيف مع باقي أجزاء المشروع بحيث لا يؤدي تعديله إلى سلسلة من الكوارث، ومدى قابلية هذا الحل إلى التوسيع بحيث لو أردنا في المستقبل أن نضيف إليه أمورا جديدة أمكننا ذلك بكل يسر، والأهم من هذا وذاك مدى موافقة هذا الحل لمواصفات الاختبارات الأحادية ليخضع لها بغرض التأكد من أنه يؤدي المطلوب بالشكل المرغوب.
توجد العديد من مبادئ التصميم مثل مبدأ DRY اختصارا ل Don’t Repeat Yourself، والذي يقضي بأن التعليمات البرمجية لا ينبغي أن تتكرر في أكثر من مكان، ويجب تفادي ذلك عبر استعمال الوظائف Methods مثلا، بحيث بدل تكرار كتابة الأوامر نقوم بتجميعها في وظيفة واحدة ونستدعيها كلما احتجنا إليها.
توجد كذلك المبادئ الخمسة الشهيرة باسم SOLID والتي سنراها بالتفصيل في مقالات قادمة إن شاء الله.

ماهي نماذج التصميم Design Patterns

نماذج التصميم هي حلول عملية تم بناؤها لتناسب وضعيات برمجية معينة، والغرض الأساسي منها هو توفير حلول برمجية لمشاكل شائعة، هذه الحلول تم اختبارها مرات ومرات وأثبتت نجاعتها، لذلك حينما تجد نفسك في وضعية برمجية من الممكن استعمال نموذج للتصميم فيها لا داعي لأن تتعب أعصابك في كتابة حل قد سبقك إليه غيرك، فقط قم بتطبيق هذا الحل واستمتع بالنتيجة.
إذن فنماذج التصميم Design Patterns هي حلول برمجية عملية عامة يمكنك استعمالها في وضعيات برمجية معينة، ويوجد العديد من نماذج التصميم مثل Repository الذي يسمح لنا بتجميع العمليات الممكن أن نقوم بها على البيانات في مكان واحد، أو مثل Singletone الذي يسمح لنا باستعمال Object واحد من نوع معين، وكذلك Factory الذي يسمح لنا بإنشاء الكائنات من أنواع أخرى، والعديد من نماذج التصميم وأشهرها GoF اختصارا ل Gang of Four وهي مجموعة من نماذج التصميم التي صاغها ثلة من المحترفين في مجال تصميم البرمجيات.

ماهو الفرق بين مبادئ ونماذج التصميم

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