المقدمة
الـ SOLID Principles عبارة عن مجموعة من القواعد البسيطة بتساعد المبرمجين على كتابة كود نظيف ومنظم وسهل الفهم والتعديل. تخيل كأنك بتبني بيت، لازم يكون كل جزء فيه له وظيفة واضحة ومكان محدد عشان البيت يبقى قوي ومستقر.
نفس الكلام في البرمجة، الكود لازم يكون منظّم عشان يسهل تطويره وصيانته!
ليه المبادئ دي مهمة؟
- كود نظيف ومنظم: بيسهل فهمه وتعديله
- صيانة أسهل: لو فيه أي مشكلة، هتلاقيها بسرعة وتصلحها
- توسعة أسرع: تقدر تضيف ميزات جديدة بسهولة
- تعاون أفضل بين المبرمجين: كل واحد هيفهم شغله كويس
- كفاءة أعلى: الكود هيشتغل بشكل أسرع وأكثر استقرارًا
مبدأ الـ Liskov Substitution
مبدأ الـ Interface Segregation
الصورة بتوضح مبدأ Interface Segregation Principle، وده واحد من المبادئ الخمسة بتوع (SOLID). الفكرة إنك ما تجبرش كائن (زي الروبوتات في الصورة) إنه يستخدم حاجات هو مش محتاجها.
الجزء الأيسر (مخالفة المبدأ):
الروبوت مطلوب منه يعمل مجموعة تمارين فيها حاجة هو مش يقدر يعملها (wiggle antennas)، فبيقول: "Oops! But I don’t have antennas" أو "يا نهار، بس أنا معنديش antennas ". هنا في مشكلة لأن الروبوت مضطر ينفذ حاجة مش على مقاسه، وده بيخالف مبدأ تقسيم الواجهات.
الجزء الأيمن (اتباع المبدأ):
التمارين اتقسمت حسب إمكانيات الروبوتات. كل روبوت بيعمل التمرين اللي يناسبه، يعني مثلاً اللي يقدر يلف يعمل تمرين لف، واللي عنده antennas. الروبوت هنا مبسوط وبيقول: "Awesome!"، وده معناه إن النظام دلوقتي بيتبع مبدأ تقسيم الواجهات لأن كل روبوت بيتعامل مع الحاجة اللي تخصه بس.
باختصار، المبدأ ده بيقول إنك تعمل واجهات صغيرة ومحددة عشان كل روبوت (أو كائن) يستخدم اللي هو محتاجه بالظبط، من غير ما يتلخبط بحاجات هو مش محتاجها.
Simple Robot Example
في المثال ده: عندنا واجهة اسمها Robot فيها كل الوظائف، حتى اللي الروبوتات مش بتحتاجها.
الروبوت اللي عنده أذرع بس (ArmRobot) لازم ينفذ كل الوظائف، حتى الوظيفة اللي ملهاش علاقة بيها زي wiggleAntennas، وده مخالف للمبدأ لأن الكائن مجبر على استخدام حاجة مش محتاجها.
الكود بعد تطبيق المبدأ:
abstract class Spinnable {
void spinAround();
}
abstract class ArmMovable {
void rotateArms();
}
abstract class AntennaWiggler {
void wiggleAntennas();
}
class ArmRobot implements Spinnable, ArmMovable {
@override
void spinAround() {
print('Robot spinning around');
}
@override
void rotateArms() {
print('Robot rotating arms');
}
}
class AntennaRobot implements Spinnable, AntennaWiggler {
@override
void spinAround() {
print('Robot spinning around');
}
@override
void wiggleAntennas() {
print('Robot wiggling antennas');
}
}
void main() {
ArmRobot armRobot = ArmRobot();
armRobot.spinAround();
armRobot.rotateArms();
AntennaRobot antennaRobot = AntennaRobot();
antennaRobot.spinAround();
antennaRobot.wiggleAntennas();
}
في المثال ده: قسمنا الواجهات لوظائف صغيرة (Spinnable، ArmMovable، و AntennaWiggler).
كل روبوت بينفذ الواجهة اللي تخص إمكانياته بس، فالروبوت اللي عنده أذرع بينفذ الواجهات اللي فيها وظائف ليه علاقة بالأذرع، والروبوت اللي عنده هوائيات بينفذ الوظائف اللي تخص الهوائيات.
النتيجة:
كل روبوت بيستخدم بس الوظائف اللي هو محتاجها، وده تطبيق صحيح لمبدأ Interface Segregation Principle.
Simple Printer Example
abstract class Printer {
void printDocument();
void scanDocument();
void print3DObject();
}
class NormalPrinter implements Printer {
@override
void printDocument() {
print('Printing document...');
}
@override
void scanDocument() {
print('Scanning document...');
}
@override
void print3DObject() {
throw Exception('This printer cannot print 3D objects');
}
}
في المثال ده: الواجهة Printer فيها كل الوظائف، حتى لو الطابعة مش بتدعمهم.
الطابعة العادية (NormalPrinter) مجبرة إنها تنفذ وظيفة print3DObject() رغم إنها مش بتدعم الطباعة ثلاثية الأبعاد. وده بيخالف مبدأ ISP لأن الطابعة بتتعامل مع حاجات مش هتستخدمها.
abstract class Printable {
void printDocument();
}
abstract class Scannable {
void scanDocument();
}
abstract class Print3D {
void print3DObject();
}
class NormalPrinter implements Printable, Scannable {
@override
void printDocument() {
print('Printing document...');
}
@override
void scanDocument() {
print('Scanning document...');
}
}
class ThreeDPrinter implements Printable, Print3D {
@override
void printDocument() {
print('Printing document...');
}
@override
void print3DObject() {
print('Printing 3D object...');
}
}
void main() {
NormalPrinter normalPrinter = NormalPrinter();
normalPrinter.printDocument();
normalPrinter.scanDocument();
ThreeDPrinter threeDPrinter = ThreeDPrinter();
threeDPrinter.printDocument();
threeDPrinter.print3DObject();
}
في المثال ده: قسمنا الواجهات بناءً على القدرات (Printable، Scannable، و Print3D).
الطابعة العادية (NormalPrinter): بتنفذ الواجهات اللي بتناسب إمكانياتها زي الطباعة والمسح الضوئي.
الطابعة الثلاثية الأبعاد (ThreeDPrinter): بتنفذ الواجهات اللي بتخص الطباعة وثلاثية الأبعاد بس.
النتيجة: كل طابعة بقت بتتعامل مع الوظائف اللي تخصها بس، وده بيحقق مبدأ Interface Segregation Principle بشكل صحيح.
Discussion