Liskov Substitution Principle¶
Definition
Subtype Requirement: \(\phi(x)\) be a property provable about objects \(x\) of type \(T\). Then \(\phi(y)\) should be true for objects \(y\) of type \(S\) where \(S\) is a subtype of \(T\).
It basically means subtypes must be behaviorally substitutable for their base types.
Benefits¶
- it gives a way to define good inheritance hierarchies
- increases caution when designing classes to avoid creation of hierarchies that do not conform to the Open-Closed-Principle.
Example
In clientMethod, superObj may be an instance of SuperClass or any of its subclasses. Hence, if clientMethod works with instances of Superlass, it does so with instances of any subclass of SuperClass. They provide all methods of SuperClass and can be extended by adding more
1 2 3 4 |
|
LSP by Example¶
LSP-non-compliant Example¶
Let assume that we want classes that represents rectangles and squares.
Since square is a rectangle designer of class might think it is a good idea to make Square
class a subclass of Rectangle
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
We override setWidth
and setHeight
so both will stay the same and we can reuse the implementation of area
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
We can pass Square
wherever Rectangle
is expected, as far as the Java type system is concerned so the model is consistent with that regard.
However, if we use such design, our program might break when client is making Rectangle
assumptions on our Squares
without knowledge that operation on the subclass is performed and the first side change will be overridden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
A Square does not comply with the behavior of a rectangle: Changing the height or width of a square is different from changing dimensions of rectangle so the model breaks Liskov Substitution Principle.
Warning
Validity of hierarchy design cannot be judged by looking at hierarchy in isolation. We need to inspect possible uses by client and its assumptions.
LSP-compliant Fix¶
- To fix the issue we can introduce
Shape
class and two descendants:Square
andRectangle
- Client of
Shape
should not make any assumptions on our setter methods. - If client want to change property of shape it should contact specific concrete class.
- It allows to calculate correct area for object of that class.
Behavioral Subtyping¶
We can look at Liskov Substitution Principle in behavioral aspect: it requires behavioral substitutability.
Behavioral Substitutability
\(S\) is a behavioral subtype of \(T\), if objects of type \(T\) in a program \(P\) may be replaced by objects of type \(S\) without altering any of the properties of \(P\).
- It is not enough that subclasses provide all methods declared in superclass, those method should behave like they were from that superclass
- subclasses have to be distinguishable by the client.
Breaking LSP in the wild¶
Even Java has classes that break LSP, one of them is Properties
.
If you are like me and want to know everything about stuff you are reading about, there is Properties article.