Liskov Substitution Principle

No discussion of inheritance would be complete without mentioning the
Liskov Substitution Principle
(LSP), the “L” in the SOLID acronym. Barbara Liskov
introduced a “substitution property” in 1988. There’s a more formal
definition, but essentially it means that if you have some code that
works with some type T and you instead give it something of type
S, then if the code continues to work correctly S is
substitutable for T.

The LSP suggests that subclasses should be substitutable for their
parent classes. That is, an instance of a subclass should be able to
be used by code that expects to work with an instance of its
superclass without fear of negative consequences.

There is
some debate
about whether this is a good principle to follow or not, but in
general I think it worth at least some consideration when designing
class hierarchies.

In a class hierarchy, applying this principle means that a subclass must not
change the expected behavior of its superclass. This is more than a
syntactic constraint: both AdCampaign and NuclearMissile might
reasonably have a method named launch, but those two methods are not
semantically equivalent and therefore don’t really satisfy the LSP.

Substitutability can be enforced using
Design by Contract.
But even if you don’t want to go that far, it’s worth looking at the
DbC rules for subtype contracts:

Preconditions cannot be strengthened in a subtype. That is, the
subtype cannot impose stricter requirements on inputs than the
supertype does.

Postconditions cannot be weakened in a subtype. That is, the subtype
must make guarantees about the output that are at least as strong as
those made by the supertype.

Invariants of the supertype must be preserved in a subtype. That
is, if the supertype guarantees that some conditions must always be
true, then the subtype must also make the same guarantees.

Thinking about these rules will help you decide if you’re violating
LSP or not.

In real code, tradeoffs are often necessary, but it’s worth thinking
about the LSP when designing a class hierarchy. If subclass methods
are doing something radically different than the superclass, or if you
have to override an inherited method in order to raise an exception
saying, “This method isn’t appropriate for this class,” there’s a
smell there and it’s worth rethinking your approach. You may decide
to keep things as-is, but do it thoughtfully after considering other
options. Even Smalltalk’s class hierarchy has some methods that send
self shouldNotImplement because the designers decided that, on
balance, that was the best approach.