- π About the author: Hello, I'm cabbage ~ ~, a sophomore in school, and a new star creator in the Java field.
- π Personal homepage: Cabbage CSDN blog
- π Series column: This article is written in the Java design pattern column: Isn't that the Java design pattern
- π§ If there are mistakes in the knowledge points of the article, please correct them! Learn and make progress with you π
- π» Motto: sunflowers grow in the sun, so do I β¨
preface
Today I learned how to use inheritance correctly -- the use of Richter's substitution principle. After learning the Richter substitution principle, I feel that it is really easy to use inheritance when learning basic java, because it is very simple and easy to use. Share your notes with you. I hope you like them π
1, Question raised
- Inheritance contains such a layer of meaning: all implemented methods in the parent class are actually setting specifications and contracts. Although it does not force all subclasses to follow these contracts, if subclasses arbitrarily modify these implemented methods, it will destroy the whole inheritance system
- Inheritance not only brings convenience to programming, but also brings disadvantages. For example, the use of inheritance will bring invasiveness to the program, reduce the portability of the program and increase the coupling between objects. If a class is inherited by other classes, all subclasses must be considered when the class needs to be modified, and all functions involving subclasses may fail after the parent class is modified
Therefore, how do we use inheritance correctly in programming? So let's look at the use of the Richter substitution principle
2, Basic introduction
- When using inheritance, follow the Richter substitution principle and try not to override the methods of the parent class in the subclass
- The Richter substitution principle tells us that inheritance actually enhances the coupling between the two classes. In appropriate cases, the problem can be solved through aggregation, composition and dependency
- If there is object O2 of type O2 for each object O1 of type O1, so that the behavior of program P does not change when all objects O1 are replaced with O2, then type O2 is a subtype of type O1. That is, all places that reference the base class must be able to use the objects of its subclasses transparently
The introduction is very abstract. Let's take a look at the detailed code comparison to help us understand the concept!
3, Code comparison
Code 1:
public class One { public static void main(String[] args) { A a = new A(); System.out.println("11-3=" + a.func1(11, 3)); System.out.println("1-8=" + a.func1(1, 8)); System.out.println("-----------------------"); B b = new B(); System.out.println("11-3=" + b.func1(11, 3));//The original intention here is to find 11-3 System.out.println("1-8=" + b.func1(1, 8));// 1-8 System.out.println("11+3+9=" + b.func2(11, 3)); } } class A { // Returns the difference between two numbers public int func1(int num1, int num2) { return num1 - num2; } } // Class B inherits class A // Added a new function: complete the addition of two numbers, and then sum with 9 class B extends A { //Here, the method of class A is rewritten, which may be unconscious public int func1(int a, int b) { return a + b; } public int func2(int a, int b) { return func1(a, b) + 9; } }
Operation results:
We can find that there is an error in the subtraction function that was running normally. The reason is simple: Class B inadvertently rewrites the method of the parent class, resulting in errors in the original function. In actual programming, we often complete new functions by rewriting the parent class. Although it is simple to write, the reusability of the whole inheritance system will be poor, especially when polymorphism runs frequently. Next, let's look at how code 2 uses the Richter substitution principle to solve the problem.
Code 2:
Improvement plan:
The original parent and child classes inherit a more popular base class. The original inheritance relationship is removed and replaced by dependency, aggregation, composition and other relationships.
Class diagram demonstration:
code:
public class Two { public static void main(String[] args) { C c = new C(); System.out.println("11-3=" + c.func1(11, 3)); System.out.println("1-8=" + c.func1(1, 8)); System.out.println("-----------------------"); D d = new D(); System.out.println("11+3=" + d.func1(11, 3));//The original intention here is to find 11 + 3 System.out.println("1+8=" + d.func1(1, 8));// 1+8 System.out.println("11+3+9=" + d.func2(11, 3)); System.out.println("11-3=" + d.func3(11, 3));// The original intention here is to find 11-3 } } //Create a more basic base class class Base { //Write more basic methods and members to the Base class } class C extends Base { public int func1(int num1, int num2) { return num1 - num2; } } // Added a new function: complete the addition of two numbers, and then sum with 9 class D extends Base { private C c = new C(); public int func1(int a, int b) { return a + b; } public int func2(int a, int b) { return func1(a, b) + 9; } public int func3(int a, int b) { return c.func1(a, b); } }
Operation results:
Code 2 first creates a Base class Base, then lets class C and class D inherit the Base class Base respectively, and finally class D regards class C as a variable, which skillfully solves the problem.
summary
When using inheritance, subclasses may accidentally write methods from the parent class, causing accidents. Therefore, when using inheritance, I must pay attention to whether the code in this place can use the Richter replacement principle to reduce unnecessary code failures!!!