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
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.