1, define,
As usual, let’s first look at the definition of Richter’s substitution principle.
All references to base classes (superclasses) must be able to transparently use objects from their subclasses. In layman’s terms, a subclass can extend the functionality of its parent class, but cannot change the functionality of its parent class.
The core idea is inheritance. By inheritance, objects that refer to a base class can be used by its subclasses. Such as:
Parent parent = new Child();
Copy the code
So how do you use it transparently?
So let’s think about the question, can a subclass change what its parent does?
public class Parent { public int add(int a, int b){ return a+b; } } public class Child extends Parent{ @Override public int add(int a, int b) { return a-b; }}Copy the code
How about that?
Must be bad, originally is addition but modified into subtraction, this is obviously not in accordance with the cognitive.
It violates the Richter’s substitution rule. When a subclass changes its parent’s functionality, we can no longer transparently use add when using a subclass in a reference to the parent class.
A method implemented in a parent class sets up a set of specifications and contracts, and while it does not force all subclasses to comply with these specifications, any modification of these nonabstract methods by subclasses can break the inheritance system.
So, the key to transparent use is that subclasses cannot change the functionality of their parent classes.
2, meaning
A subclass can implement abstract methods of its parent class, but cannot override non-abstract methods of its parent class.
As we said earlier, a subclass cannot change the functionality of its parent, so a subclass cannot override nonabstract methods of its parent.
A subclass can implement the superclass’s abstract methods, must be, that it’s supposed to implement.
package com.fanqiekt.principle.liskov.rapper; /** * @author: Public abstract class BaseRapper {/** * freeStyle */ protected abstract void freeStyle(); /** * protected void playBeat(){system.out.println ("..."){/** * protected void playBeat(){system.out.println ("... ); } /** * freeStyle */ public void perform(){playBeat(); freeStyle(); }}Copy the code
BaseRapper is an abstract class that represents the base class of Rapper.
Rappers usually perform by playing a random accompaniment and then performing free style.
FreeStyle was different, so it was written as an abstract method and left to the subclasses.
The playBeat process is mostly the same, playing the accompaniment randomly from the library, so it’s written as a non-abstract method.
Most of the perform flow is the same, you play the accompaniment, and then freestyle, you also write it as a non-abstract method.
package com.fanqiekt.principle.liskov.rapper; /** * Rapper ** @author */ public class Rapper extends BaseRapper {/** * subclass extends BaseRapper */ @override Protected void playBeat() {system.out.println (" Turn off the microphone "); } @override public void perform() {system.out.println (" ghost step "); } /** * Override protected void freeStyle() {system.out.println ("... "); ); }}Copy the code
Rapper is a subclass of BaseRapper that overrides its parent class’s abstract method freeStyle.
The nonabstract method playBeat overrides the parent class and changes the logic to turn on the microphone, clearly violating the Richter substitution principle. This is clearly the wrong way to write it, because the behavior of the parent class is not consistent with the behavior of the child class, so you can’t use the parent class transparently. You’re not kidding me, are you?
I tried to modify the playBeat.
/ @override protected void playBeat() {super.playbeat (); System.out.println(" Turn off the microphone "); }Copy the code
Is it ok to call super in a subclass method?
No, because there is no logical connection between opening the microphone and playing the accompaniment.
When subclasses are used transparently, the accompaniment plays normally, but the microphone is turned off without the caller’s knowledge, and the closing of the microphone is obviously unrelated to the accompaniment. There is no real transparency to the caller.
It also overrides the parent’s nonabstract method Perform and changes the logic to dance if it violates the Richter substitution rule. Is a show that only dances but not rappers still called rappers?
I’m trying to modify Perform.
*/ @override public void perform() {super.perform(); System.out.println(" ghost step "); }Copy the code
So, the perform method, can I change it like this?
So why do subclasses call super, why don’t they call playBeat, perform?
Perform; perform; perform; perform; perform; perform;
Quiet freestyle or dancing freestyle, for the caller, is freestyle.
2. Subclasses can add their own special methods.
An important feature of inheritance is that subclasses can add methods when they inherit from their parent class.
Public void dance(){system.out.println (" dance! "); ); }Copy the code
In rappers, you can add the dance method.
3. When a subclass overrides a method of its parent class, its preconditions (that is, its parameters) are looser than the input parameters of the parent method.
Note that a subclass overrides its parent, not the other way around.
Overloading is equivalent to a completely new method that does not conflict with the parent’s method of the same name. Both exist at the same time, and the method is automatically selected based on the passed parameter.
Abstract methods can be overridden as well as non-abstract methods.
Why should method parameters be looser than their parent?
First of all, the parameters are definitely not consistent, and if they are, we’re rewriting them, and we’re back to the first meaning.
Second, what would happen if we were stricter?
We can look at the following example.
package com.fanqiekt.principle.liskov.rapper; import java.util.List; Public void setList(List<String> List){public void setList(List<String> List){ System.out.println(" Execute the parent setList method "); }}Copy the code
This is the parent class, and the setList method has a parameter of type List. >
package com.fanqiekt.principle.liskov.rapper; import java.util.ArrayList; Public class Children extends Parent {public void setList(ArrayList<String> list) { System.out.println(" Execute subclass setList method "); }}Copy the code
This is a subclass, passing in arguments of type ArrayList, which is more strict than the parent class.
Children children = new Children();
children.setList(new ArrayList<>());
Copy the code
So let’s run this line of code and see what happens.
Execute the subclass setList methodCopy the code
Is there anything wrong with this result?
The problem is that setList(new ArrayList<>()) should transparently execute the setList(List List) method of the parent class according to the Richter substitution principle.
This is a bit confusing, for the caller, because I wanted to call Parent’s setList(List List) method, but ended up executing Children’s setList(ArrayList List) method instead.
This is as if the subclass overrides the parent class’s setList method instead of overloading the subclass’s setList method.
That is, a method whose parameters are strict will at some point become overwritten.
Rewriting is obviously not in accordance with Richter’s substitution principle.
So let’s look at the loose version.
Public class Children extends Parent {public void setList(Collection<String> list) { System.out.println(" Execute subclass setList method "); }}Copy the code
Subclasses, passing in arguments of type Collection, are looser than the parent class.
Children children = new Children();
children.setList(new ArrayList<>());
Copy the code
Again, let’s run this line of code and see what happens.
Execute the parent setList methodCopy the code
Children children = new Children();
children.setList(new HashSet<>());
Copy the code
Again, let’s run this line of code and see what happens.
Execute the subclass setList methodCopy the code
The passed parameter type is more lenient, allowing subclasses to override the parent class.
4. When a subclass’s method implements an abstract method of the parent class, the method’s postcondition (that is, the method’s return value) is stricter than the parent class’s.
Note that we are talking about overriding abstract methods; non-abstract methods cannot be overridden.
Why is the return value stricter when a subclass implements an abstract method from its parent class?
package com.fanqiekt.principle.liskov.rapper; import java.util.List; Public abstract class Parent {public abstract List<String> getList(); public abstract class Parent {public abstract List<String> getList(); }Copy the code
The parent class has an abstract method getList that returns a List.
package com.fanqiekt.principle.liskov.rapper; import java.util.List; Public class Children extends Parent {@override public Collection<String> getList() {return new ArrayList<>(); }}Copy the code
Subclass getList returns a Collection type, which is a looser type.
There will be a red line:… ★ ★ No need to use incompatible return type.
The parent class returns a List, and the subclass returns a List’s parent class Collection. When using the parent class transparently, you need to convert a Collection to a List. Class up-casting is safe, but down-casting is not necessarily safe.
package com.fanqiekt.principle.liskov.rapper; import java.util.List; Public class Children extends Parent {@override public ArrayList<String> getList() {return new ArrayList<>(); }}Copy the code
Subclass getList returns the ArrayList type, which is more strict.
Converting an ArrayList to a List, upcasting is safe.
2,
Chef of eight major cuisines
Tomato Restaurant, after careful and conscientious operation, from a small restaurant into a large restaurant.
Cook: boss, we now big business big customer flow is also big, although I am full of energy, but I also can’t stand so many people’s destruction.
Boss: Vandalized? Are you sure?
Cook: no, you heard wrong, it is to take care of, can not stand the care of so many people.
Boss: Small Turkey, can ah, strong desire for life. So what do you have in mind?
Chef: I think we can introduce chefs of eight major cuisines. First, we can hand over the dishes of any cuisine to the chefs of any cuisine. The taste and quality will be better, and only then can we match the restaurant of such high specifications.
Boss: Well, you have a point there. Go on.
Cook: two, more hands, but also can increase the speed of food, three to……
Boss: that makes sense. We’re hiring a chef right now, little Turkey. Congratulations, you’ve been promoted and you’re the future head chef. Because you really want to live.
Chef: Thanks, Boss. (Heart: I have a strong desire for life? What’s stronger? After school you don’t go, I let you taste my fierce, to make you a table of good dishes)
The desire for life is really strong.
3, implementation,
No nonsense, dick code.
package com.fanqiekt.principle.liskov; Public void cook(String) {public void cook(String) {public void cook(String) {public void cook(String) DishName){system.out.println (" start cooking: "+dishName); cooking(dishName); System.out.println(dishName + ""); } /** * start cooking */ protected void cooking(String dishName); }Copy the code
Abstract cook classes, public cook methods, are responsible for some of the same logic that cooks do, such as starting the preparation of cooking, and leaving the pot.
Specific cooking details provide an abstract method of cooking (cooking), which cooks of specific cuisines need to rewrite.
package com.fanqiekt.principle.liskov; Public ShanDongChef extends Chef{@override protected void cooking(String dishName) { Switch (dishName){case "cookingTomato(); break; Default: throw new IllegalArgumentException(" Unknown food "); }} private void cookingTomato() {system.out.println (system.out.println); System.out.println(" refry tomato "); System.out.println("..." ); }}Copy the code
Shandong cuisine Chef ShanDongChef inherited the abstract Chef class Chef and realized the abstract cooking method.
package com.fanqiekt.principle.liskov; /** * @author */ public class extends Chef{@override protected void cooking(String dishName) { Switch (dishName){case "potato ": cookingPotato(); break; Default: throw new IllegalArgumentException(" Unknown food "); }} /** * private void cookingPotato() {system.out.println (); System.out.println(" put the hash "); System.out.println("..." ); }}Copy the code
SiChuanChef inherits Chef abstract Chef and implements abstract cooking method.
package com.fanqiekt.principle.liskov; /** * public void order(String dishName){** * public void order(String dishName){ System.out.println(" guest order: "+ dishName); Chef chef = new SiChuanChef(); Switch (dishName) {case "chef ": new ShanDongChef(); break; Case "chef = new SiChuanChef(); break; } chef.cook(dishName); System.out.println(dishName + "serve, please taste!") ); }}Copy the code
The Waiter has an order method to notify the chef of the corresponding cuisine to prepare the food.
Richter’s substitution rule is used here. The parent class Chef can be referenced transparently using the subclass ShanDongChef or SiChuanChef.
package com.fanqiekt.principle.liskov; /** ** * @author */ public class Client {public static void main(String args[]){Waiter Waiter = new Waiter(); Waiter. Order (" tomato and egg "); System.out.println("---------------"); Waiter. Order (" hot and sour potatoes "); }}Copy the code
So let’s run it.
Order: Scrambled eggs with tomatoes Start cooking: Scrambled eggs with tomatoes first scrambled eggs then scrambled tomatoes... Tomato scrambled eggs out of the pot tomato scrambled eggs on the table, please taste! -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the guest order: hot and sour potatoes to start cooking, hot and sour potatoes First put onion ginger and garlic Put the potatoes... Hot and sour potato shreds out of the pot hot and sour potato shreds on the table, please taste!Copy the code
4, strengths,
After polishing through the code, we found several advantages to the substitution principle.
The core idea of Richter’s substitution principle is inheritance, so merit is inherited merit.
Code reuse By inheriting a parent class, we can reuse a lot of code, such as the preparation of the cook and the cooking.
Reducing the cost of creating classes, each subclass has the attributes and methods of its parent class.
Easy to maintain and easy to extend By inheritance, subclasses make it easier to extend functionality.
It’s also easier to maintain, with common methods in the parent class, and specific methods in specific subclasses.
5 and disadvantages
As seen above, its disadvantage is the disadvantage of inheritance.
Breaking encapsulated inheritance is intrusive, so it tightly couples subclasses with their parents.
The inability of a subclass to change its parent may result in redundant code and reduced flexibility for subclasses because subclasses have all the methods and attributes of their parent.
Hip hop says
Next, please enjoy the original song by Richter’s Substitution principle.
Hip Hop says: The Richter Substitution Principle "Beat freestyle" can be played in the library by the rappers next door. They can play it decently and have fun Refer to the parent class transparent using subclass will make the code more powerful Subclasses can have their own special way More of the broad masses of overloading parameters when the parent class Or may cover the superclass method Rewrite the abstract method return value types to down Because class up conversion can put the heart down Eight big cuisines every cook has his or her specialty those of the basic skills to master The advantage is that it is easy to extend and maintain and automatically inherits from the parent classCopy the code
To try it, click here
Idle no matter to listen to music, knowledge has filled the brain to;
Learning new ways to review, wearing headphones is a big deal.