مبادئ SOLID في JavaScript: النظرية والتطبيق من الألف إلى الياء
\n\nتطوير برمجيات عالية الجودة لا يتطلب فقط معرفة بتركيب اللغة، بل أيضاً فهم مبادئ الهندسة المعمارية. SOLID هي خمسة مبادئ أساسية للتصميم كائني التوجه تساعد في إنشاء كود مرن وقابل للصيانة والتوسع. في هذه المقالة، سنشرح كل مبدأ من مبادئ SOLID في سياق JavaScript (Node.js) مع أمثلة عملية.
\n\nما هو SOLID؟
\n\nتم تقديم اختصار SOLID بواسطة روبرت مارتن (العم بوب) ويتضمن المبادئ التالية:
\n\n- \n
- S — مبدأ المسؤولية الواحدة (Single Responsibility Principle) \n
- O — مبدأ الفتح/الإغلاق (Open/Closed Principle) \n
- L — مبدأ استبدال ليسكوف (Liskov Substitution Principle) \n
- I — مبدأ فصل الواجهات (Interface Segregation Principle) \n
- D — مبدأ عكس التبعيات (Dependency Inversion Principle) \n
هذه المبادئ عالمية وقابلة للتطبيق على أي لغة برمجة، بما في ذلك JavaScript. دعنا ننظر إلى كل منها بالتفصيل.
\n\n1. مبدأ المسؤولية الواحدة (SRP)
\n\nالتعريف: يجب أن يكون للفئة أو الوحدة سبب واحد فقط للتغيير. بمعنى آخر، كل وحدة مسؤولة عن مهمة واحدة محددة.
\n\nمثال سيء: فئة UserService المسؤولة عن حفظ المستخدم، وإرسال البريد الإلكتروني، وإنشاء التقارير.
// ❌ انتهاك SRP\nclass UserService {\n saveUser(user) {\n // حفظ في قاعدة البيانات\n console.log('User saved');\n }\n\n sendEmail(user) {\n // إرسال البريد الإلكتروني\n console.log('Email sent to ' + user.email);\n }\n\n generateReport(user) {\n // إنشاء تقرير\n console.log('Report generated for ' + user.name);\n }\n}\n\nمثال جيد: نقوم بتقسيم المسؤوليات إلى فئات منفصلة.
\n\n// ✅ الامتثال لـ SRP\nclass UserRepository {\n save(user) {\n console.log('User saved');\n }\n}\n\nclass EmailService {\n send(user) {\n console.log('Email sent to ' + user.email);\n }\n}\n\nclass ReportGenerator {\n generate(user) {\n console.log('Report generated for ' + user.name);\n }\n}\n\n// الاستخدام\nconst user = { name: 'إيفان', email: 'ivan@example.com' };\nconst userRepo = new UserRepository();\nconst emailService = new EmailService();\nconst reportGen = new ReportGenerator();\n\nuserRepo.save(user);\nemailService.send(user);\nreportGen.generate(user);\n\nالآن كل فئة لها مسؤولية واحدة، والتغييرات في وحدة واحدة لا تؤثر على الوحدات الأخرى.
\n\n2. مبدأ الفتح/الإغلاق (OCP)
\n\nالتعريف: يجب أن تكون الكيانات البرمجية (الفئات، الوحدات، الدوال) مفتوحة للتمديد ولكن مغلقة للتعديل.
\n\nمثال سيء: دالة تحسب مساحة الأشكال باستخدام عبارات شرطية.
\n\n// ❌ انتهاك OCP\nfunction calculateArea(shape) {\n if (shape.type === 'circle') {\n return Math.PI * shape.radius ** 2;\n } else if (shape.type === 'rectangle') {\n return shape.width * shape.height;\n }\n // عند إضافة شكل جديد، يجب تعديل الدالة\n return 0;\n}\n\nمثال جيد: نستخدم تعدد الأشكال والوراثة.
\n\n// ✅ الامتثال لـ OCP\nclass Shape {\n area() {\n throw new Error('يجب تنفيذ طريقة area()');\n }\n}\n\nclass Circle extends Shape {\n constructor(radius) {\n super();\n this.radius = radius;\n }\n\n area() {\n return Math.PI * this.radius ** 2;\n }\n}\n\nclass Rectangle extends Shape {\n constructor