مبادئ SOLID في JavaScript: من النظرية إلى التطبيق على Node.js
SOLID هي خمسة مبادئ أساسية في التصميم الكائني التوجُّه، صاغها روبرت مارتن (العم بوب). تساعد هذه المبادئ في إنشاء كود مرن، قابل للصيانة، وقابل للتوسع. على الرغم من أن الكثيرين يربطون SOLID بلغات الـ OOP التقليدية (Java, C#)، إلا أنها قابلة للتطبيق بشكل ممتاز في JavaScript أيضًا، خاصة عند التطوير على Node.js. في هذه المقالة، سنشرح كل مبدأ بأمثلة عملية.
مبدأ المسؤولية الواحدة (Single Responsibility Principle, SRP)
ينص مبدأ SRP على: «يجب أن يكون للفئة (أو الوحدة) سبب واحد فقط للتغيير». وهذا يعني أن كل وحدة مسؤولة عن مهمة واحدة محددة.
مثال سيء (انتهاك مبدأ SRP):
class UserService { createUser(data) { /* منطق الإنشاء */ } sendWelcomeEmail(email) { /* إرسال البريد */ } logActivity(action) { /* التسجيل */ }}هنا، فئة واحدة مسؤولة عن العمل مع المستخدمين، وإرسال رسائل البريد الإلكتروني، والتسجيل. إذا تغير تنسيق السجلات، فسيتعين تعديل UserService — على الرغم من أن مهمته الأساسية لم تتغير.
مثال جيد (الامتثال لمبدأ SRP):
class UserRepository { createUser(data) { /* فقط العمل مع قاعدة البيانات */ }}
class EmailService { sendWelcomeEmail(email) { /* فقط الإرسال */ }}
class Logger { logActivity(action) { /* فقط التسجيل */ }}
class UserService { constructor(userRepo, emailService, logger) { this.userRepo = userRepo; this.emailService = emailService; this.logger = logger; } createUser(data) { const user = this.userRepo.createUser(data); this.emailService.sendWelcomeEmail(user.email); this.logger.logActivity('User created'); return user; }}الآن كل فئة مسؤولة عن مجال مسؤوليتها الخاص. من خلال دمجها في UserService، نحصل على مرونة وعزل للتغييرات.
مبدأ الانفتاح/الإغلاق (Open/Closed Principle, OCP)
ينص مبدأ OCP على: «يجب أن تكون الكيانات البرمجية مفتوحة للتمديد، ولكن مغلقة للتعديل». أي يجب أن نكون قادرين على إضافة وظائف جديدة دون تغيير الكود الموجود.
مثال سيء (انتهاك مبدأ OCP):
class PaymentProcessor { processPayment(type, amount) { if (type === 'card') { /* معالجة البطاقة */ } else if (type === 'paypal') { /* معالجة PayPal */ } // عند إضافة نوع جديد — يجب تعديل هذه الطريقة }}مثال جيد (الامتثال لمبدأ OCP):
class PaymentStrategy { pay(amount) { throw new Error('Must be implemented'); }}
class CardPayment extends PaymentStrategy { pay(amount) { console.log(`Paid ${amount} via Card`); }}
class PayPalPayment extends PaymentStrategy { pay(amount) { console.log(`Paid ${amount} via PayPal`); }}
class PaymentProcessor { constructor(strategy) { this.strategy = strategy; } processPayment(amount) { this.strategy.pay(amount); }}
// الاستخدامconst cardPayment = new PaymentProcessor(new CardPayment());cardPayment.processPayment(100);
const paypalPayment = new PaymentProcessor(new PayPalPayment());paypalPayment.processPayment(200);الآن، لإضافة طريقة دفع جديدة، يكفي إنشاء فئة جديدة ترث من PaymentStrategy، وتمريرها إلى PaymentProcessor. الكود الأصلي لا يتغير.
مبدأ استبدال باربرا ليكوف (Liskov Substitution Principle, LSP)
ينص مبدأ LSP على: «يجب أن تكون الكائنات في البرنامج قابلة للاستبدال بنماذج من أنواعها الفرعية دون التأثير على صحة تنفيذ البرنامج». بعبارة أخرى، إذا كان لديك فئة أساسية