Design principle - Richter substitution principle

concept

  • Liskov Substitution Principle (LSP): if a software entity applies to a parent class, it must apply to its child class. All places that reference the parent class must be able to use the objects of its child class transparently. The child class object can replace the parent class object, and the program logic remains unchanged.

  • Richter replacement principle table: if a base class object is replaced with its subclass object in the software, the program will not produce any errors and exceptions, otherwise it will not be established; If a software entity uses a subclass object, it may not be able to use a base class object. For example, Zhang San likes animals. Zhang San must like dogs because dogs are a subclass of animals; If Zhang San likes dogs, it can't be concluded that Zhang San likes animals.

  • Advantage 1: the overflow of constraint inheritance is an embodiment of the opening and closing principle.

  • Advantage 2: strengthen the robustness of the program. At the same time, it can achieve very good compatibility when changing, and improve the maintainability and expansibility of the program. Reduce the risk introduced when requirements change.


code

example

  • Verify that the square is not a special rectangle
  • Rectangle.java
/**
 * @Description rectangle
 * @date Dec 15, 2021
 * @Version 1.0
 */
public class Rectangle {

    private long length;
    private long width;

    public long getLength() {
        return length;
    }

    public void setLength(long length) {
        this.length = length;
    }

    public long getWidth() {
        return width;
    }

    public void setWidth(long width) {
        this.width = width;
    }
}
  • Square.java
/**
 * @Description square
 * @date Dec 15, 2021
 * @Version 1.0
 */
public class Square extends Rectangle{

    /**
     * Side length
     */
    private long sideLength;

    public long getSideLength() {
        return sideLength;
    }

    public void setSideLength(long sideLength) {
        this.sideLength = sideLength;
    }

    @Override
    public long getLength() {
        return getSideLength();
    }

    @Override
    public void setLength(long length) {
        setSideLength(length);
    }

    @Override
    public long getWidth() {
        return getSideLength();
    }

    @Override
    public void setWidth(long width) {
        setSideLength(width);
    }
}
  • Test.java
/**
 * @Description Test class
 * @date Dec 15, 2021
 * @Version 1.0
 */
public class Test {

    /**
     * Resize
     * When width is less than or equal to length, give width + 1 until they are equal
     * @param rectangle
     */
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
            System.out.println("width: " + rectangle.getWidth() + " length: " + rectangle.getLength());
        }

        System.out.println("resize end, width: " + rectangle.getWidth() + " length: " + rectangle.getLength());
    }
}
  • Rectangle (base class) validation
public static void main(String[] args) {
    Rectangle rectangle = new Rectangle();
    rectangle.setWidth(10);
    rectangle.setLength(20);
    resize(rectangle);
}
  • The output is as follows, and the program executes normally:
width: 11 length: 20
width: 12 length: 20
width: 13 length: 20
width: 14 length: 20
width: 15 length: 20
width: 16 length: 20
width: 17 length: 20
width: 18 length: 20
width: 19 length: 20
width: 20 length: 20
width: 21 length: 20
resize end, width: 21 length: 20
  • Square (subclass) validation
/**
 * Square (subclass) validation
 * @param args
 */
public static void main(String[] args) {
    Square square = new Square();
    square.setLength(10);
    resize(square);
}
  • The output is as follows. The method will go on indefinitely until overflow. When we replace the parent class with a child class for execution, the expectation of program operation is different from what we expect. The program design here violates the Richter replacement principle
width: 11 length: 11
width: 12 length: 12
width: 13 length: 13
width: 14 length: 14
width: 15 length: 15
width: 16 length: 16
width: 17 length: 17
width: 18 length: 18
width: 19 length: 19
width: 20 length: 20
width: 21 length: 21
width: 22 length: 22
width: 23 length: 23
width: 24 length: 24
width: 25 length: 25
width: 26 length: 26
width: 27 length: 27
width: 28 length: 28
....................

Richter substitution principle

  • Create a new quadrilateral class and break the inheritance relationship between rectangle and square
  • Quadrangle.java
/**
 * @Description quadrilateral
 * @date Dec 19, 2021
 * @version 1.0
 */
public interface Quadrangle {

    long getWidth();

    long getLength();

}
  • Rectangle.java
/**
 * @Description rectangle
 * @date Dec 15, 2021
 * @Version 1.0
 */
public class Rectangle implements Quadrangle {

    private long length;
    private long width;

    @Override
    public long getWidth() {
        return width;
    }

    @Override
    public long getLength() {
        return length;
    }

    public void setLength(long length) {
        this.length = length;
    }

    public void setWidth(long width) {
        this.width = width;
    }
}
  • Square.java
/**
 * @Description square
 * @date Dec 15, 2021
 * @Version 1.0
 */
public class Square implements Quadrangle {

    /**
     * Side length
     */
    private long sideLength;

    @Override
    public long getWidth() {
        return sideLength;
    }

    @Override
    public long getLength() {
        return sideLength;
    }

    public long getSideLength() {
        return sideLength;
    }

    public void setSideLength(long sideLength) {
        this.sideLength = sideLength;
    }
}
  • Test.java

  • It can be seen that through the quadrilateral, the get method is ok, but the set method is not. Because the quadrilateral interface does not declare the set length and width, the secret is that the parent class does not have an assignment method, and the whole resize method is not applicable to the quadrilateral type. A constraint is proposed in this method to prohibit the overflow of inheritance

summary

  • Richter substitution principle is one of the important ways to realize the opening and closing principle. Since subclass objects can be used wherever base class objects are used, try to use base class types to define objects in the program, and determine their subclass types at run time to replace parent class objects with subclass objects

  • When applying the Richter substitution principle, the parent class should be designed as an abstract class or interface, so that the child class inherits the parent class or implements the parent interface and implements the methods declared in the parent class. When the program runs, the child class instance replaces the parent class instance, which can easily expand the function of the system without modifying the code of the original child class. Adding new functions can be realized by adding new child classes.


Source code


- End - -Personal study notes-

Added by GaryAC on Mon, 10 Jan 2022 01:18:01 +0200