مقدمة: لماذا تعتبر مبادئ SOLID مهمة لمطور TypeScript
SOLID هي خمسة مبادئ أساسية للبرمجة كائنية التوجه، صاغها روبرت مارتن (العم بوب). في سياق TypeScript، الذي يجمع بين الكتابة الثابتة ومرونة JavaScript، فإن تطبيق مبادئ SOLID يسمح بإنشاء تطبيقات قابلة للتوسع والاختبار والصيانة بسهولة. في هذه المقالة، سنشرح كل مبدأ مع الأساس النظري والأمثلة العملية باستخدام TypeScript.
1. مبدأ المسؤولية الواحدة (Single Responsibility Principle - SRP)
النظرية
يجب أن يكون للفصل سبب واحد فقط للتغيير. وهذا يعني أن كل وحدة أو فصل أو دالة مسؤولة عن وظيفة واحدة بالضبط.
التطبيق العملي في TypeScript
لننظر إلى انتهاك مبدأ SRP من خلال مثال لفصل يدير المستخدمين وإرسال الإشعارات:
// ❌ انتهاك مبدأ SRPclass UserManager { constructor(private name: string, private email: string) {}
saveUser(): void { console.log(`تم حفظ المستخدم ${this.name} في قاعدة البيانات`); }
sendWelcomeEmail(): void { console.log(`تم إرسال بريد ترحيبي إلى ${this.email}`); }}النسخة المصححة مع فصل المسؤوليات:
// ✅ الامتثال لمبدأ SRPclass User { constructor(public name: string, public email: string) {}}
class UserRepository { save(user: User): void { console.log(`تم حفظ المستخدم ${user.name} في قاعدة البيانات`); }}
class EmailService { sendWelcomeEmail(user: User): void { console.log(`تم إرسال بريد ترحيبي إلى ${user.email}`); }}2. مبدأ الفتح/الإغلاق (Open/Closed Principle - OCP)
النظرية
يجب أن تكون الكيانات البرمجية مفتوحة للتمديد ولكن مغلقة للتعديل. بمعنى آخر، يجب أن تكون قادرًا على إضافة وظائف جديدة دون تغيير الكود الموجود.
التطبيق العملي في TypeScript
مثال لحساب الخصومات لأنواع مختلفة من العملاء:
// ❌ انتهاك مبدأ OCPclass DiscountCalculator { calculateDiscount(customerType: string, amount: number): number { if (customerType === 'regular') { return amount * 0.1; } else if (customerType === 'premium') { return amount * 0.2; } else if (customerType === 'vip') { return amount * 0.3; } return 0; }}// ✅ الامتثال لمبدأ OCP من خلال تعدد الأشكالinterface DiscountStrategy { calculate(amount: number): number;}
class RegularDiscount implements DiscountStrategy { calculate(amount: number): number { return amount * 0.1; }}
class PremiumDiscount implements DiscountStrategy { calculate(amount: number): number { return amount * 0.2; }}
class DiscountCalculator { constructor(private strategy: DiscountStrategy) {}
calculate(amount: number): number { return this.strategy.calculate(amount); }}3. مبدأ استبدال ليسكوف (Liskov Substitution Principle - LSP)
النظرية
يجب أن تكون الكائنات في البرنامج قابلة للاستبدال بنماذج من أنواعها الفرعية دون تغيير صحة البرنامج. ببساطة: إذا كان لديك فصل أساسي، يجب أن يعمل أي من مشتقاته بشكل صحيح في كل مكان يُستخدم فيه الفصل الأساسي.
التطبيق العملي في TypeScript
المثال الكلاسيكي مع الأشكال الهندسية:
// ❌ انتهاك مبدأ LSPclass Rectangle { constructor(protected width: number, protected height: number) {}
setWidth(width: number): void { this.width = width; }
setHeight(height: number): void { this.height = height; }
getArea(): number { return this.width * this.height; }}
class Square extends Rectangle { setWidth(width: number): void { this.width = width; this.height = width; // انتهاك! }
setHeight(height: number): void