This is the first post in a series of three about the Liskov Substitution Principle. The other posts focuses on LSP in combination with covariance and contravariance so I will not dwell on those topics here.
The Liskov Substitution Principle LSP states that:
Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.)
This boils down to some requirements on the type signature:
- Contravariance of method arguments in the subtype.
- Covariance of return types in the subtype.
- No new exceptions should be thrown by methods of the subtype, except where those exceptions are themselves subtypes of exceptions thrown by the methods of the supertype.
Let’s learn a little more about the Liskov Substitution Principle by investigating a design problem and during that process break the law…
Let’s say that you run a fruit store and you have created a general interface for fruits:
A concrete implementation of the Fruit
interface could look like so:
You decide to implement a calculator so that the customer can calculate the number of fruits and calories consumed during the day. The Person
object functions as an aggregator.
The calculator can be used as follows:
Ackee is a fruit, however it is poisonous if it is not prepared in the correct way. Since it’s used for medical purposes it could still be that you sell it in the store.
So it could be that we implement the Fruit
api like this for Ackee
(no need to calculate the calories since it should not be eaten):
If we by mistake consumes an Ackee
fruit and then try to calculate our daily intake of calories we get the following result:
One could argue that getting an Exception is the least of our problems at the moment, but there is no argue in that we broke the law (the Liskov Substitution Principle that is).
As mentioned above the Liskov Substitution Principle LSP states that:
Substitutability is a principle in object-oriented programming stating that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e. an object of type T may be substituted with any object of a subtype S) without altering any of the desirable properties of the program (correctness, task performed, etc.)
In this case we introduced an Exception
in one of the subtypes (Ackee
) which causes our calculator to crash, i.e. breaking the law.
So how could we avoid the problem? Lets introduce a new interface called EatableFruit
and move the calories function to it:
We now implement the subtypes like this:
We have to update the calculator:
And we use the calculator like this:
It no longer possible to send Ackee
to the calculator since it is not an eatable fruit.
The code examples implemented in Kotlin are available at: github