admin管理员组

文章数量:1200344

The Liskov substitution principle states that a piece of code should remain correct if a superclass is replaced with its subclass (paraphrasing). What I don't understand; Why do we care about this property? Does it make our code more reusable, flexible , or maintainable ?

I would appreciate an example piece of code that adheres to the principle and one that violates it when explaining the relevance of substitution property.

The Liskov substitution principle states that a piece of code should remain correct if a superclass is replaced with its subclass (paraphrasing). What I don't understand; Why do we care about this property? Does it make our code more reusable, flexible , or maintainable ?

I would appreciate an example piece of code that adheres to the principle and one that violates it when explaining the relevance of substitution property.

Share Improve this question edited Jan 21 at 21:05 Mark Seemann 233k49 gold badges447 silver badges773 bronze badges asked Jan 21 at 19:40 ariko stephenariko stephen 958 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 3

The Liskov substitution principle states that a piece of code should remain correct if a superclass is replaced with its subclass

If this was the only thing that the principle stated, it hardly makes sense to ask why we care about it. Consider what the opposite of that principle would mean: That a piece of code should not remain correct if a superclass is replaced with its subclass?

Who would want their software to be incorrect?

What the Liskov Substitution Principle (LSP) actually says is that a a subclass may widen preconditions and tighten postconditions, but not the other way around.

This makes good sense, if you think about it. Consider a simple method that takes an integer as input.

void Foo(int i)

If the superclass states that every integer i is valid, then what would happen if a subclass tightened the precondition? You might, for example, implement a subclass that divides some number by i. If you do that, however, the input value 0 would now cause a division-by-zero error in the subclass, and that error wouldn't happen in the superclass.

Client code relies on the contract published by the superclass, since it doesn't know which subclass it might be interacting with.

As the opposite example, imagine that the superclass has a precondition that says that i mustn't be 0. This means that no well-behaved client is going to call the Foo method with a 0 argument. If you then implement a subclass that can handle 0, no harm is done. Thus, you can widen the precondition, but not tighten it.

Similar considerations apply to postconditions, just in the opposite direction. Here, a subclass may tighten a postcondition, but not widen it.

You can see some realistic C# examples in my article The Liskov Substitution Principle as a profunctor.

Violation of the Liskov substitution principle leads to incorrect execution of the program.


Here is an example of such a violation:

class Rectangle {
    private int width;
    private int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int area() {
        return width * height;
    }
}
class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }

    @Override
    public void setHeight(int height) {
        super.setWidth(height);
        super.setHeight(height);
    }
}
// This code will not work correctly for instance of Square
void test(Rectangle rectangle) {
    rectangle.setWidth(5);
    rectangle.setHeight(10);

    assertEquals(50, rectangle.area());
}

本文标签: oopUsefulness of the substitution property of the Liskov Substitution PrincipleStack Overflow