Liskov Substitution Principle, abbreviated as LSP. This principle was first proposed in 1986 by Barbara Liskov, who describes it this way:

If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program.

A subclass object can replace a parent object wherever it appears in the program, and the logic behavior of the original program remains intact and its correctness is not compromised.

How to understand the Li substitution principle

The above explanation is abstract, but let’s use a simple example to explain it. The parent class, Thransporter, uses the network for simple message transfer. Subclass AuthThransporter inherits Thransporter and adds token validation when transferring messages.

/** * Transporter */
public class Transporter {

    public void send(Request request) {
        // Send a message...}}/** * AuthTransporter */
public class AuthTransporter extends Transporter {
    private String token;

    public AuthTransporter(String token) {
      this.token = token;
    }

    public void send(Request request) {
        // Add token validation to the request
        request.setHeader("token", token);
        // Send a message...}}public class Message {

    public void doSend(Transporter transporter) {
      // omit some code...
      transporter.send(request);
    }
}

Message message = new Message();
// The substitution principle
message.doSend(new AuthTransporter(token));
Copy the code

In the code above, the subclass AuthTransporter is designed to fully comply with the in-substitution principle. It can replace the parent class wherever it appears and keep the logical behavior of the original code unchanged, correct, and intact.

See here, partners may have such doubts, just the code design is not the use of object-oriented polymorphic characteristics? Is li substitution the same thing as polymorphism? From the example and definition description, the Li substitution principle and polymorphism look somewhat similar, but in fact they say two different things.

Polymorphism is a feature of object-oriented programming and a syntax of object-oriented programming language. It is an idea of code implementation. The interior substitution is a design principle, which is used to guide how to design the subclass in the inheritance relationship. The design of the subclass should ensure that when replacing the parent class, the logic of the original program is not changed and the correctness of the original program is not damaged.

Once again, we modify the Send () function in the AuthTransporter class slightly to validate the token passed in.

/** * AuthTransporter */
public class AuthTransporter extends Transporter {
    private String token;

    public AuthTransporter(String token) {
      this.token = token;
    }

    public void send(Request request) {
      if (StringUtils.isBank(this.token)) {
        throw new AuthException("No token");
      }
      // Add token validation to the request
      request.setHeader("token", token);
      // Send a message...}}Copy the code

In the modified code, if message.dosend () is passed to the subclass AuthTransporter, it is possible to throw an exception, but if message.dosend () is passed to the parent Transporter, it is not. When a subclass replaces its parent by passing message.dosend (), the logical behavior of the entire program changes.

Although the modified code can still use Java polymorphic syntax to dynamically replace the parent class with the subclass AuthTransporter, it does not cause errors in compiling or running the program. However, in terms of design ideas, the design of AuthTransporter violates the formula substitution principle.

What code complies with the li substitution principle

A more practical and instructive description of the Li substitution principle is “Design by convention.” What does that mean? Subclasses are designed to follow the behavior conventions of their parent class. The parent class defines the behavior convention of a function, and the subclass can change the internal implementation logic of a function, but not the original behavior convention of the function. The behavior conventions here include: function declaration to implement the function; Conventions for inputs, outputs, and exceptions; Even any special instructions listed in the notes. In fact, the relationship between the parent and subclass in the definition can also be replaced by the relationship between the interface and the implementation class.

In order to make your friends better understand, here are a few counterexamples to explain.

A subclass violates what its parent class declares to implement

A subclass overrides the sortOrdersByDate() order sorting function to sort orders by value. The design of that subclass violates the substitution rule.

A subclass violates its parent’s convention on inputs, outputs, and exceptions

In a parent class, a function convention returns NULL on an error; Returns an empty collection if the retrieved data is empty. When a subclass overloads the function, the implementation changes, returning exception on an error or NULL if no data is retrieved. The design of that subclass violates the substitution rule.

In a parent class, a function convention allows input data to be any integer, but the implementation of a subclass only allows input data to be a positive integer, negative values are thrown out, that is, the subclass is stricter on the input data than the parent class, then the design of the subclass violates the inside substitution principle.

In a parent class, a function convention will throw only NotFoundException. The design implementation of that subclass will only throw NotFoundException. Throwing any other exception will cause the subclass to violate the substitution principle.

Subclasses violate any special instructions listed in the parent class comments

If a subclass overwrites getBook() to get all books (including logically deleted books), the subclass is not designed to meet the rule of substitution.

These are three typical violations of the Li substitution principle. Another tip to determine if a subclass’s design implementation violates the li substitution rule is to validate the subclass’s code with the parent’s unit tests. If some unit test runs fail, it is possible that the design implementation of the subclass does not fully comply with the conventions of the parent class, and that the subclass may violate the inner substitution principle.

The last

The Li substitution principle is a principle that guides how subclasses should be designed in inheritance relationships. When the parent class defines the convention of the function, the subclass can change the internal implementation logic of the function, but cannot change the original convention of the function, that is, to ensure that when replacing the parent class, the logic of the program does not change and does not destroy the correctness of the original program.