مبادئ SOLID في Java: من النظرية إلى التطبيق
SOLID هو اختصار لخمسة مبادئ أساسية في البرمجة كائنية التوجه، صاغها روبرت مارتن (العم بوب). تساعد هذه المبادئ المطورين على إنشاء كود مرن وقابل للصيانة وسهل التوسع. في هذه المقالة، سنشرح كل مبدأ من مبادئ SOLID مع أمثلة بلغة Java، من النظرية البسيطة إلى التطبيق العملي الحقيقي.
1. مبدأ المسؤولية الواحدة (SRP)
النظرية: يجب أن يكون للفصل سبب واحد فقط للتغيير. بمعنى آخر، يجب أن يكون كل فصل مسؤولاً عن وظيفة واحدة فقط.
انتهاك SRP:
public class Employee { public void calculatePay() { /* حساب الراتب */ } public void saveToDatabase() { /* الحفظ في قاعدة البيانات */ } public void generateReport() { /* إنشاء تقرير */ }}التصحيح:
public class Employee { /* البيانات فقط */ }
public class PayCalculator { public void calculatePay(Employee emp) { /* ... */ }}
public class EmployeeRepository { public void save(Employee emp) { /* ... */ }}
public class ReportGenerator { public void generate(Employee emp) { /* ... */ }}الآن كل فصل لديه مسؤولية واحدة واضحة، مما يبسط الاختبار والصيانة.
2. مبدأ الفتح/الإغلاق (OCP)
النظرية: يجب أن تكون الكيانات البرمجية (الفصول، الوحدات، الدوال) مفتوحة للتمديد ولكن مغلقة للتعديل.
انتهاك OCP:
public class ShapeDrawer { public void draw(String shapeType) { if ("circle".equals(shapeType)) { // رسم دائرة } else if ("rectangle".equals(shapeType)) { // رسم مستطيل } }}التصحيح باستخدام تعدد الأشكال:
public interface Shape { void draw();}
public class Circle implements Shape { @Override public void draw() { /* رسم دائرة */ }}
public class Rectangle implements Shape { @Override public void draw() { /* رسم مستطيل */ }}
public class ShapeDrawer { public void draw(Shape shape) { shape.draw(); }}الآن لإضافة شكل جديد (مثل Triangle) لا حاجة لتعديل الكود الموجود — فقط ننشئ فصلاً جديداً ينفذ الواجهة Shape.
3. مبدأ استبدال ليسكوف (LSP)
النظرية: يجب أن تكون الكائنات في البرنامج قابلة للاستبدال بنماذج من أنواعها الفرعية دون تغيير صحة البرنامج. إذا كان S نوعاً فرعياً من T، فيمكن استبدال كائنات من نوع T بكائنات من نوع S دون تعطيل العمل.
انتهاك LSP الكلاسيكي:
public class Rectangle { protected int width, height; public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } public int getArea() { return width * height; }}
public class Square extends Rectangle { @Override public void setWidth(int w) { super.setWidth(w); super.setHeight(w); } @Override public void setHeight(int h) { super.setWidth(h); super.setHeight(h); }}العميل الذي يعمل مع Rectangle يتوقع أن تغيير العرض لا يؤثر على الارتفاع. المربع ينتهك هذا التوقع. الحل: استخدام واجهة مشتركة أو فصول منفصلة دون وراثة.
4. مبدأ فصل الواجهات (ISP)
النظرية: يجب ألا يعتمد العملاء على طرق لا يستخدمونها. من الأفضل وجود عدة واجهات متخصصة بدلاً من واجهة واحدة "سميكة".
انتهاك ISP: