In Defence of the Liskov Substitution Principle

The Liskov Substitution Principle (LSP for short) is the “L” in the SOLID principles. Even though many people know this principle, at least by name, I cannot keep track of how many times it is skipped when the SOLID principles are being explained. Most of the time, the explanation of the LSP is reduced to stating its name and showing a half-assed example to which nobody can relate!

Perceived Complexity

I think that the LSP is often overlooked because of its perceived complexity, starting with its name. Indeed, this principle is named after the computer science pioneer and Turing Award winner Barbara Liskov, who laid the base for our understanding of modules and abstraction. As such, it is the only SOLID principle that has not a self-explaining name and I believe that it could throw people off.

Funnily enough, Barbara Liskov explained - during a conference talk she gave in 2020 - that someone asked her once what this principle meant and she was pretty confused because she wasn’t even aware of its existence.

It’s Just Semantics

In a nutshell, the LSP means that there must be a semantic relationship between a class and a subclass - sharing methods with the same name is not enough. For instance, stacks and queues may both have add or remove methods but these classes should not inherit from each other as these methods mean completely different things: a stack would work in a last-in-first-out (LIFO) fashion as opposed to first-in-first-out (FIFO) for a queue. Consequently, if your program is built using a queue and you substitute it for a stack, it might not work anymore.

Temper your Expectations

The idea behind the LSP revolves around expectations and contracts. If I use a stack, I expect it to be a LIFO and would not be happy if it turned out to be a FIFO instead. Any subtype of the stack should respect this contract.

We can therefore infer that a subtype violates the LSP if

  • it expects more than what the contract states (e.g. non-null input, specific concrete type for an interface, etc.)
  • it does less than what the contract requires (e.g. return null when it shouldn’t, does not return a list in proper order)
  • it does not implement a method defined in the base class or interface.

The LSP also means that the client code should not rely on something that is not in the contract.