This is a very interesting article.
Open/Close principle
Defining a subclass does not break the open/close principle, since it means "open for extension / closed for modification". So:
- One can perfectly add
Penguin
subclass to extend Bird
- But changing the interface of the
Bird
class to add support for birds that can't fly is definitively a modification.
The Bird class was flawed from the beginning
What does the example shows us ? A very practical case of how to deal with a flawed design. And if the design is not viable, you have to change it.
The Bird
class was flawed since the beginning, because it didn't take into account the fact that there are birds that can't fly, be it the whole class Penguin
or a single instance of injuredBird
.
The class is even more flawed than it seems. Because all birds have ranges of altitude: kingfishers can have a slightly negative altitude under water. And no birds can't fly in the stratosphere: Some species can fly up to 11.300 km but some others are limited to 8.000. And Penguins
are limited between 0 and 0 above the ground (in fact they can dive, so negative altitude is allowed also.
Liskov Substitution Principle in all this ?
Looking at the example code, adding Penguin
to the original Bird
is in fact not a problem according to the LSP, because no promise is made on the result of setAltitude()
.
The LSP tells us that:
- preconditions cannot be strengthened.
- postconditions cannot be weakened. There is no claim made that the altitude will be set to the value provided, so there's no weakening. If the postconditon is that the new value of altitude is between 0 and the value provided, the extension would match the rules by leaving the altitude unchanged. Btw.us, this post-condition would also allow for
injuredBird
and newBornBird
.
- invariants must be preserved.
- history constraint shall be ensured. This implies that the state and behavior of a
Bird
only depend on the use of its defined interface. So for example, Penguin
could have no other method that would change the altitude without relying on setAltitude()
.