Emad Saif نشر 23 مايو 2022 أرسل تقرير نشر 23 مايو 2022 سمعت منذ فترة عن SOLID وعندما بحثت عنها وجدت أنها تُستخدم لجعل الكود أفضل. ما هي مبادئ SOLID بالضبط، وفي ماذا تُتسعمل؟ وهل يجب أن أستخدمها في كل مشروع أقوم به؟ 1 اقتباس
2 سامح أشرف نشر 23 مايو 2022 أرسل تقرير نشر 23 مايو 2022 مبادئ SOLID أو SOLID principles هي مجموعة من القواعد النظرية التي تهدف لجعل الكود أسهل في الكتابة والصيانة، وتساعدك على العمل في مشاريع كبيرة بدون بذل جهد كبير في عملية إصلاح الأخطاء، وهذه المبادئ مفيدة بصورة خاصّة في التصميم كائنيّ التوجّه object-oriented design، حيث تقدم هذه القواعد الأساسيات التي يجب إتباعها عند إنشاء الأصناف وربطها معًا. هذه المبادئ ليست ضرورية لتصميم البرمجيات ولكنها تساعد مطوّري البرمجيّات على تحقيق تصاميم برمجيّة عالية الجودة. كل حرف من أحرف كلمة SOLID عبارة عن أختصار لأحد المبادئ: مبدأ المسؤولية الواحدة Single Responsibility Principle (SRP) مبدأ الفتح والإغلاق Open/Closed Principle (OCP) مبدأ ليسكوف للاستبدال Liskov Substitution Principle (LSP) مبدأ فصل الواجهات Interface Segregation Principle (ISP) مبدأ عكس التابعيّة Dependency Inversion Principle (DIP) هنا شرح بسيط لكل مبدأ من مبادئ SOLID: مبدأ المسؤولية الواحدة Single Responsibility Principle (SRP) ينص هذا المبدأ على التالي: اقتباس يجب ألّا يكون هناك أكثر من سبب واحد لتغيير صنف class ما. There should never be more than one reason for class to change على سبيل المثال إن كان لديك الصنف User للتعامل مع بيانات المستخدم في قاعدة البيانات، وبفرض أن هذا الصنف مسؤول عن عرض البيانات في الصفحة الشخصية للمستخدم، فسيكون لدينا سببين للتعديل على هذا الصنف في المستقبل: عندما نُريد عمل تغير ما في بيانات المستخدم في قاعدة البيانات. عندما نريد تغير شكل البيانات التي يتم عرضها في الصفحة الشخصية للمستخدم وبالتالي يوجد أكثر من سبب يدفعنا للذهاب إلى هذا الصنف والتعديل عليه، أي أن هذا الصنف لا يُحقق مبدأ المسؤولية الواحدة Single Responsibility Principle، ويجب فصله إلى صنفين كلُ منهما مسؤول عن أمر واحد ومحدد. يمكنك القراءة أكثر عن هذا المبدأ من خلال هذه المقالة: مبدأ الفتح والإغلاق Open/Closed Principle (OCP) المبدأ الثاني ضمن مبادئ SOLID هو مبدأ الفتح والإغلاق Open/Closed Principle، وينص هذا المبدأ على التالي: اقتباس يجب أن تكون كيانات entities البرنامج (الأصناف، الوحدات البرمجيّة modules، الدوال functions، ...الخ) مفتوحةً للتوسّع ومغلقةً للتعديل. Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. الهدف من هذا المبدأ هو التفكير في المستقبل (البناء الآن، التخطيط للمستقبل) بحيث نصمم البرمجيات وجعلها لا تحتاج إلى تغيير الكود المكتوب مسبقًا عند إضافة ميزات ووظائف جديدة إليها. على سبيل المثال إن كان لديك صنف Shape يصف أي شكل مُضلع ثنائي البعد، وقمت بإضافة الخواص width و height و area و perimeter فيه، فهذا الصنف لا يُحقق مبدأ الفتح والإغلاق Open/Closed Principle، لأننا سنحتاج إلى تعديل هذا الصنف في حالة أردنا عمل مثلث أو شكل خماشي، حيث أن هذه الأشكال لديها مُحيط perimeter ومساحة area ولكن ليس لديها طول وعرض. يمكنك القراءة أكثر عن هذا المبدأ من خلال هذه المقالة: مبدأ ليسكوف للاستبدال Liskov Substitution Principle (LSP) سُمي هذا المبدأ باسم صاحبه البروفسور باربارا ليسكوف، الذي طرحته لأوّل الأمر عام 1987. يهدف هذا المبدأ إلى تحديد شكل عملية الوراثة inheritance بين الأصناف، ويمكننا صياغة هذا المبدأ بالشكل التالي: اقتباس عند إعادة تعريف إجراء في نوع ابن، يمكننا فقط، استبدال شروطه البادئة preconditions بشروط أضعف، وشروطه اللّاحقة postconditions بشروط أقوى. when redefining a routine [in a derivative], you may only replace its precondition by a weaker one, and its postcondition by a stronger one. لفهم معنى هذا المبدأ، تخيل أن لدينا صنف Rectangle والذي يصف أي مستطيل، ويوجد في هذا الصنف تابع يقوم بتعيّن الطول setHeight وتابع يقوم بتعين العرض setWidth، حتى الآن لا يوجد مشكلة، ولكن عندما نريد عمل صنف Square ويرث من الصنف Rectangle (حيث أن المربع حالة خاصة من المستطيل (مستطيل طوله مساوي لعرضه))، فسنواجه مشكلة في هذا الأمر، لأن المربع لا يمكن أن يحتوي على طول مختلف عن العرض، ولهذا يجب عمل صنف مختلف تمامًا للمربع Square بدون أن يرث من Rectangle يمكنك القراءة أكثر عن هذا المبدأ من خلال هذه المقالة: مبدأ فصل الواجهات Interface Segregation Principle (ISP) هدف هذا المبدأ هو التفكير في المستقيل كذلك، ولكن بطريقة مختلفة عن مبدأ المسؤولية الوحيدة السابق ذكره، حيث ينص هذا المبدأ على التالي: اقتباس لا ينبغي إجبار المستخدمين على استخدام واجهات لا يحتاجونها. Clients should not be forced to depend upon interfaces that they do not use. لفهم نص هذا المبدأ، يجب أن تفهم الواجهات Interfaces في لغة PHP (أو في البرمجة بشكل عام)، هنا مقالة توفر شرح لهذا الأمر بالإضافة لبعض المفاهيم الأساسية الأخرى: الآن تخيل أن لدينا واجهة تصف الشخص الواحد بالشكل التالي: interface iHuman { public function work(); // الإنسان يمكنه العمل public function sleep($hours); // يمكن أن ينام لعدة ساعات public function eat(); // يمكنه أن يأكل } بعد ذلك نريد أن نقوم بعمل صنف لموظف employee وبالتأكيد سوف نستخدم الواجهة السابقة بالشكل التالي: class Employee implements iHuman { public function work() {} public function sleep($hours) {} public function eat() {} } حتى الآن لا يوجد مشكلة، لكن ماذا إذا أردنا عمل صنف يُعبر عن طفل رضيع صغير Baby (ليس لديه عمل Work)، لذلك سوف يتم إجبارنا على إضافة التابع work إلى الصنف Baby حتى وإن لم يكن له فائدة تُذكر، لذلك ظهر مبدأ فصل الواجهات والذي ينص على فصل الواجهات عن بعضها البعض، بالشكل التالي: interface iWorkable { public function work(); // الإنسان يمكنه العمل } interface iHuman { public function sleep($hours); // يمكن أن ينام لعدة ساعات public function eat(); // يمكنه أن يأكل } class Employee implements iHuman, iWorkable { public function work() {} public function sleep($hours) {} public function eat() {} } class Baby implements iHuman { public function sleep($hours) {} public function eat() {} } يمكنك القراءة أكثر عن هذا المبدأ من خلال هذه المقالة: مبدأ عكس التابعيّة Dependency Inversion Principle (DIP) هذا المبدأ هو آخر مبادئ التصميم الكائنيّ SOLID، قد يكون صعبًا قليلًا لكنه مفيد للغاية، خصوصًا في البرمجيات الكبيرة والتي تحتوي على أصناف كثيرة ومتعددة، وينص على التالي: اقتباس أ – لا ينبغي أن تعتمد الوحدات البرمجيّة عالية المستوى على الوحدات البرمجيّة منخفضة المستوى. يجب على كلّ منهما الاعتماد على واجهات (أصناف مجرّدة). ب – لا ينبغي أن تعتمد الواجهات/الأصناف المُجردّة على التفاصيل، فالتفاصيل هي من يجب أن تعتمد على الأصناف الواجهات. A. High level modules should not depend upon low level modules. Both should depend upon abstractions. B. Abstractions should not depend upon details. Details should depend upon abstractions. تخيل أن لديك صنف Button وصنف Window وتقوم بإستخدام كائنات من الصنف Button في العديد من الأمكان داخل الصنف Window، تكمن المشكلة الآن في أن الصنف Window أصبح يعتمد بشكل أساسي عل ىالصنف Button، وإذا أردت إجراء بعض التعديلات على الصنف Button (تغير بعض التوابع أو حذفها أو نغير المعاملات التي تأخذها)، فيجب أن تقوم بالتعديل على الصنف Window في الأماكن التي تستعمل كائنات من نوع Button ليجاري هذه التغيرات ويعمل بشكل سليم. حل المشكلة هنا هو عمل واجهة interface تسمى iButton على سبيل المثال، يعتمد عليها كلُ من الصنفين Button و Window، وبالتالي لن يعتمد أحد الأصناف على صنف آخر. يمكنك القراءة أكثر عن هذا المبدأ من خلال هذه المقالة: 3 اقتباس
السؤال
Emad Saif
سمعت منذ فترة عن SOLID وعندما بحثت عنها وجدت أنها تُستخدم لجعل الكود أفضل.
ما هي مبادئ SOLID بالضبط، وفي ماذا تُتسعمل؟
وهل يجب أن أستخدمها في كل مشروع أقوم به؟
1 جواب على هذا السؤال
Recommended Posts
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.