مبدأ استبدال ريتش ، OCP ، باعتباره مبدأ OO رفيع المستوى ، يدعو إلى استخدام "التجريد" و "تعدد الأشكال" لتغيير البنية الثابتة في التصميم إلى بنية ديناميكية للحفاظ على حاوية التصميم. "الملخص" هي وظيفة توفرها اللغة. يتم تنفيذ "تعدد الأشكال" بواسطة دلالات موروثة.
يحتوي مبدأ استبدال Richter على المعاني الأربعة التالية:
الآن يمكننا شرح المعاني الأربعة المذكورة أعلاه.
يمكن للفئات الفرعية تنفيذ أساليب مجردة لفئة الوالدين ، ولكن لا يمكنها تجاوز أساليب غير المجردة لفئة الوالدين.
عندما نقوم بتصميم أنظمة ، غالبًا ما نقوم بتصميم واجهات أو فئات مجردة ، ثم تنفذ الفئات الفرعية طرقًا مجردة. يتم استخدام مبدأ استبدال Richter بالفعل هنا. من السهل أن نفهم أن الفئات الفرعية يمكنها تنفيذ الطريقة التجريدية لفئة الأصل. في الواقع ، يجب على الفئات الفرعية تنفيذ الطريقة المجردة بالكامل لفئة الأصل ، حتى لو كانت تكتب طريقة فارغة ، وإلا فإنها ستجمع ويبلغ عن خطأ.
النقطة الرئيسية لمبدأ استبدال Richter هي أنه لا يمكن أن يغطي أساليب غير المجردة للطبقة الأصل. أي طريقة جيدة التنفيذ في الفئة الأصل هي في الواقع تعيين سلسلة من المواصفات والعقود. على الرغم من أنها لا تجبر جميع الفئات الفرعية على الامتثال لهذه المواصفات ، إذا تعدل الفئة الفرعية بشكل تعسفي هذه الأساليب غير المجردة ، فإنها ستضر بنظام الميراث بأكمله. مبدأ استبدال ليزور يعبر عن هذا المعنى.
في فكرة التصميم الموجهة نحو الكائن ، فإن ورث هذه الميزة تجلب راحة كبيرة لتصميم النظام ، ولكن هناك أيضًا بعض المخاطر التي تأتي منها. يتم استخدام الأمثلة التالية لتوضيح خطر الميراث. نحتاج إلى إكمال وظيفة طرح رقمين ، والفئة A مسؤولة عنها.
الفئة A {public int func1 (int a ، int b) {return ab ؛ }} client client {public static void main (string [] args) {a a = new a () ؛ System.out.println ("100-50 ="+A.Func1 (100 ، 50)) ؛ System.out.println ("100-80 ="+A.Func1 (100 ، 80)) ؛ }} نتائج التشغيل:
100-50 = 50100-80 = 20
في وقت لاحق ، نحتاج إلى إضافة وظيفة جديدة: أكمل إضافة رقمين ، ثم يلخصها بـ 100 ، والفئة B مسؤولة. أي أن الفئة ب تحتاج إلى إكمال وظيفتين:
يطرح الرقمين.
أضف رقمين ثم أضف 100.
نظرًا لأن الفئة A نفذت الوظيفة الأولى ، بعد أن ترث الفئة B من الفئة A ، تحتاج فقط إلى إكمال الوظيفة الثانية. الرمز كما يلي:
يمتد الفئة B A {public int func1 (int a ، int b) {return a+b ؛ } public int func2 (int a ، int b) {return func1 (a ، b) +100 ؛ }} client client {public static void main (string [] args) {b b = new b () ؛ System.out.println ("100-50 ="+B.Func1 (100 ، 50)) ؛ System.out.println ("100-80 ="+B.Func1 (100 ، 80)) ؛ System.out.println ("100+20+100 ="+B.Func2 (100 ، 20)) ؛ }} بعد اكتمال الفئة B ، نتيجة التشغيل:
100-50 = 150100-80 = 180100+20+100 = 220
وجدنا أن وظيفة الطرح التي كانت تعمل في الأصل كانت عادة خطأ. والسبب هو أنه عندما تسمي الفئة B الطريقة ، يعيد كتابة طريقة الفئة الأصل بطريق الخطأ ، مما تسبب في جميع الرموز التي تعمل على تشغيل وظائف الطرح للاتصال بطريقة إعادة الكتابة للفئة B ، مما يسبب أخطاء في الوظيفة العادية الأصلية. في هذا المثال ، بعد الإشارة إلى الوظيفة المكتملة حسب الفئة A الأساسية والتغيير إلى الفئة الفرعية B ، حدث استثناء. في البرمجة الفعلية ، غالبًا ما نكممل وظائف جديدة عن طريق إعادة كتابة طريقة فئة الأصل. على الرغم من أنه من السهل الكتابة ، إلا أن إعادة استخدام نظام الميراث بأكمله سيكون ضعيفًا نسبيًا ، خاصةً عند استخدام تعدد الأشكال بشكل متكرر ، فإن احتمال أخطاء تشغيل البرنامج مرتفع للغاية. إذا كان عليك إعادة كتابة طريقة فئة الأصل ، فإن النهج الأكثر شيوعًا هو: ترث فئة الوالدين الأصلية والطفل فئة أساسية أكثر شعبية ، وإزالة علاقة الميراث الأصلي ، واستخدام التبعية والتجميع والمجموعة والعلاقات الأخرى بدلاً من ذلك.