Rich's replacement principle, OCP, as the high-level principle of OO, advocates the use of "abstraction" and "polymorphism" to change the static structure in the design into a dynamic structure to maintain the enclosure of the design. "Abstract" is a function provided by the language. "Polymorphism" is implemented by inherited semantics.
The Richter replacement principle contains the following 4 meanings:
Now we can explain the above four meanings.
Subclasses can implement abstract methods of parent class, but cannot override non-abstract methods of parent class.
When we are designing systems, we often design interfaces or abstract classes, and then subclasses implement abstract methods. The Richter replacement principle is actually used here. It is easy to understand that subclasses can implement the abstract method of the parent class. In fact, subclasses must fully implement the abstract method of the parent class, even if they write an empty method, otherwise they will compile and report an error.
The key point of the Richter substitution principle is that it cannot cover the non-abstract methods of the parent class. Any well-implemented method in the parent class is actually setting a series of specifications and contracts. Although it does not force all subclasses to comply with these specifications, if the subclass arbitrarily modify these non-abstract methods, it will damage the entire inheritance system. The principle of Lizur replacement expresses this meaning.
In the object-oriented design idea, inheriting this feature brings great convenience to the design of the system, but there are also some risks that come from it. The following examples are used to illustrate the risk of inheritance. We need to complete the function of subtracting two numbers, and class A is responsible for it.
class A{ public int func1(int a, int b){ return ab; } } public class 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)); } } Running results:
100-50=50100-80=20
Later, we need to add a new function: complete the addition of two numbers, and then sum it with 100, and class B is responsible. That is, class B needs to complete two functions:
The two numbers subtract.
Add two numbers and then add 100.
Since class A has implemented the first function, after class B inherits class A, you only need to complete the second function. The code is as follows:
class B extends A{ public int func1(int a, int b){ return a+b; } public int func2(int a, int b){ return func1(a,b)+100; } } public class 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)); } } After class B is completed, the run result:
100-50=150100-80=180100+20+100=220
We found that the subtraction function that was originally running normally had an error. The reason is that when class B named the method, it accidentally rewrites the method of the parent class, causing all codes that run subtraction functions to call the rewrite method of class B, causing errors in the original normal function. In this example, after referring to the function completed by base class A and changing to subclass B, an exception occurred. In actual programming, we often complete new functions by rewriting the parent class method. Although it is simple to write, the reusability of the entire inheritance system will be relatively poor, especially when polymorphism is used more frequently, the probability of program operation errors is very high. If you have to rewrite the parent class method, the more common approach is: the original parent class and child class inherit a more popular base class, remove the original inheritance relationship, and use dependency, aggregation, combination and other relationships instead.