مصطلحات ستعيش معها في مجال تصميم البرمجيات 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 لهذا المبدأ.

التعليقات