في جافا، مبادئ راسخة هي منهج موجه للكائنات يتم تطبيقه على تصميم هيكل البرنامج. يتم تصوره بواسطة روبرت سي مارتن (المعروف أيضًا باسم العم بوب). لقد غيرت هذه المبادئ الخمسة عالم البرمجة الشيئية، كما غيرت طريقة كتابة البرامج. كما يضمن أيضًا أن يكون البرنامج معياريًا وسهل الفهم وتصحيح الأخطاء وإعادة البناء. في هذا القسم سنناقش المبادئ الصلبة في جافا مع المثال المناسب .
اختصار كلمة SOLID لـ:
- مبدأ المسؤولية الفردية (SRP)
- المبدأ المفتوح والمغلق (OCP)
- مبدأ استبدال ليسكوف (LSP)
- مبدأ فصل الواجهة (ISP)
- مبدأ انعكاس التبعية (DIP)
دعونا نشرح المبادئ واحدة تلو الأخرى بالتفصيل.
محدد جافا
مبدأ المسؤولية الفردية
ينص مبدأ المسؤولية الفردية على ذلك يجب أن تؤدي كل فئة Java وظيفة واحدة . قد يؤثر تنفيذ وظائف متعددة في فئة واحدة على دمج الكود وإذا كان هناك أي تعديل مطلوبًا على الفصل بأكمله. إنه دقيق للكود ويمكن الحفاظ على الكود بسهولة. دعونا نفهم مبدأ المسؤولية الفردية من خلال مثال.
يفترض، طالب هي فئة لها ثلاث طرق وهي طباعة التفاصيل ()، حساب النسبة المئوية ()، و addStudent(). وبالتالي، تقع على عاتق فصل الطلاب ثلاث مسؤوليات تتمثل في طباعة تفاصيل الطلاب وحساب النسب المئوية وقاعدة البيانات. باستخدام مبدأ المسؤولية الفردية، يمكننا فصل هذه الوظائف إلى ثلاث فئات منفصلة لتحقيق هدف المبدأ.
Student.java
public class Student { public void printDetails(); { //functionality of the method } pubic void calculatePercentage(); { //functionality of the method } public void addStudent(); { //functionality of the method } }
ينتهك مقتطف الكود أعلاه مبدأ المسؤولية الفردية. لتحقيق هدف المبدأ، يجب علينا تنفيذ فئة منفصلة تؤدي وظيفة واحدة فقط.
Student.java
public class Student { public void addStudent(); { //functionality of the method } }
PrintStudentDetails.java
public class PrintStudentDetails { public void printDetails(); { //functionality of the method } }
النسبة المئوية.جافا
public class Percentage { public void calculatePercentage(); { //functionality of the method } }
ومن ثم، فقد حققنا هدف مبدأ المسؤولية الفردية من خلال فصل الوظيفة إلى ثلاث فئات منفصلة.
مبدأ مفتوح ومغلق
يقوم التطبيق أو الوحدة النمطية بتضمين الأساليب والوظائف والمتغيرات وما إلى ذلك. وينص المبدأ المفتوح والمغلق على ذلك وفقًا للمتطلبات الجديدة يجب أن تكون الوحدة مفتوحة للتمديد ولكنها مغلقة للتعديل. يتيح لنا الامتداد تنفيذ وظائف جديدة للوحدة. دعونا نفهم المبدأ من خلال مثال.
يفترض، معلومات المركبة هي فئة ولها الطريقة عدد المركبات() الذي يقوم بإرجاع رقم السيارة.
معلومات المركبة.java
public class VehicleInfo { public double vehicleNumber(Vehicle vcl) { if (vcl instanceof Car) { return vcl.getNumber(); if (vcl instanceof Bike) { return vcl.getNumber(); } }
إذا أردنا إضافة فئة فرعية أخرى باسم Truck، فببساطة، نضيف عبارة if أخرى تنتهك مبدأ الفتح والإغلاق. الطريقة الوحيدة لإضافة فئة فرعية وتحقيق الهدف الأساسي من خلال تجاوز عدد المركبات() الطريقة كما بينا فيما يلي.
معلومات المركبة.java
public class VehicleInfo { public double vehicleNumber() { //functionality } } public class Car extends VehicleInfo { public double vehicleNumber() { return this.getValue(); } public class Car extends Truck { public double vehicleNumber() { return this.getValue(); }
وبالمثل، يمكننا إضافة المزيد من المركبات عن طريق إنشاء فئة فرعية أخرى تمتد من فئة المركبات. لن يؤثر هذا النهج على التطبيق الحالي.
مبدأ استبدال ليسكوف
تم تقديم مبدأ استبدال ليسكوف (LSP) بواسطة باربرا ليسكوف . ينطبق على الميراث بطريقة تجعل يجب أن تكون الفئات المشتقة قابلة للاستبدال تمامًا لفئاتها الأساسية . بمعنى آخر، إذا كانت الفئة A هي نوع فرعي من الفئة B، فيجب أن نكون قادرين على استبدال B بـ A دون مقاطعة سلوك البرنامج.
إنه يوسع مبدأ الإغلاق المفتوح ويركز أيضًا على سلوك الطبقة المتفوقة وأنواعها الفرعية. يجب علينا تصميم الفئات للحفاظ على الممتلكات ما لم يكن لدينا سبب قوي للقيام بخلاف ذلك. دعونا نفهم المبدأ من خلال مثال.
Student.java
public class Student { private double height; private double weight; public void setHeight(double h) { height = h; } public void setWeight(double w) { weight= w; } ... } public class StudentBMI extends Student { public void setHeight(double h) { super.setHeight(h); super.setWeight(w); } public void setWeight(double h) { super.setHeight(h); super.setWeight(w); } }
انتهكت الفئات المذكورة أعلاه مبدأ استبدال Liskov لأن فئة StudentBMI لها قيود إضافية، مثل الطول والوزن الذي يجب أن يكون متماثلًا. لذلك، لا يمكن استبدال فئة الطالب (الفئة الأساسية) بفئة StudentBMI (الفئة المشتقة).
ومن ثم، فإن استبدال فئة Student بفئة StudentBMI قد يؤدي إلى سلوك غير متوقع.
مبدأ فصل الواجهة
وينص المبدأ على أن الواجهات الأكبر تنقسم إلى واجهات أصغر. لأن فئات التنفيذ تستخدم فقط الطرق المطلوبة. لا ينبغي لنا أن نجبر العميل على استخدام الأساليب التي لا يريد استخدامها.
الهدف من مبدأ فصل الواجهة مشابه لمبدأ المسؤولية الفردية. دعونا نفهم المبدأ من خلال مثال.
لنفترض أننا أنشأنا واجهة اسمها تحويل وجود ثلاث طرق إنتتودوبل ()، إنتتوشار ()، و شارتوسترينغ () .
سلسلة صفيف ج
public interface Conversion { public void intToDouble(); public void intToChar(); public void charToString(); }
الواجهة المذكورة أعلاه لها ثلاث طرق. إذا أردنا استخدام طريقة intToChar() فقط، فليس لدينا خيار لتطبيق طريقة واحدة. للتغلب على المشكلة، يسمح لنا المبدأ بتقسيم الواجهة إلى ثلاث واجهات منفصلة.
public interface ConvertIntToDouble { public void intToDouble(); } public interface ConvertIntToChar { public void intToChar(); } public interface ConvertCharToString { public void charToString(); }
الآن يمكننا استخدام الطريقة المطلوبة فقط. لنفترض أننا نريد تحويل العدد الصحيح إلى مزدوج والحرف إلى سلسلة، فسنستخدم الطرق فقط إنتتودوبل () و charToString().
public class DataTypeConversion implements ConvertIntToDouble, ConvertCharToString { public void intToDouble() { //conversion logic } public void charToString() { //conversion logic } }
مبدأ انعكاس التبعية
ينص المبدأ على أنه يجب علينا استخدام التجريد (الفئات والواجهات المجردة) بدلاً من التطبيقات الملموسة. لا ينبغي أن تعتمد الوحدات عالية المستوى على الوحدة ذات المستوى المنخفض ولكن يجب أن يعتمد كلاهما على التجريد. لأن التجريد لا يعتمد على التفصيل بل التفصيل يعتمد على التجريد. إنه يفصل البرنامج. دعونا نفهم المبدأ من خلال مثال.
public class WindowsMachine { //functionality }
ومن الجدير، إذا لم يكن لدينا لوحة المفاتيح والماوس للعمل على نظام التشغيل Windows. لحل هذه المشكلة، نقوم بإنشاء مُنشئ للفئة وإضافة مثيلات لوحة المفاتيح والشاشة. بعد إضافة الحالات، يبدو الفصل كما يلي:
public class WindowsMachine { public final keyboard; public final monitor; public WindowsMachine() { monitor = new monitor(); //instance of monitor class keyboard = new keyboard(); //instance of keyboard class } }
يمكننا الآن العمل على جهاز يعمل بنظام Windows بمساعدة لوحة المفاتيح والماوس. لكننا لا نزال نواجه المشكلة. لأننا قمنا بربط الفئات الثلاثة معًا بإحكام باستخدام الكلمة الأساسية الجديدة. من الصعب اختبار جهاز windows الخاص بالفصل.
لجعل الكود مقترنًا بشكل غير محكم، نقوم بفصل WindowsMachine عن لوحة المفاتيح باستخدام واجهة لوحة المفاتيح وهذه الكلمة الأساسية.
Keyboard.java
public interface Keyboard { //functionality }
WindowsMachine.java
public class WindowsMachine { private final Keyboard keyboard; private final Monitor monitor; public WindowsMachine(Keyboard keyboard, Monitor monitor) { this.keyboard = keyboard; this.monitor = monitor; } }
في الكود أعلاه، استخدمنا حقن التبعية لإضافة تبعية لوحة المفاتيح إلى فئة WindowsMachine. ولذلك، قمنا بفصل الطبقات.
لماذا يجب علينا استخدام المبادئ الصلبة؟
- فهو يقلل من التبعيات بحيث يمكن تغيير كتلة من التعليمات البرمجية دون التأثير على كتل التعليمات البرمجية الأخرى.
- تهدف المبادئ إلى جعل التصميم أسهل ومفهومًا.
- باستخدام المبادئ، يصبح النظام قابلاً للصيانة والاختبار والتطوير وإعادة الاستخدام.
- إنه يتجنب التصميم السيئ للبرنامج.
في المرة القادمة عندما تقوم بتصميم برنامج، ضع هذه المبادئ الخمسة في الاعتبار. من خلال تطبيق هذه المبادئ، ستصبح التعليمات البرمجية أكثر وضوحًا وقابلية للاختبار وقابلة للاستهلاك.