Find Inner Peace With Liskov Substitution Principle

We’re successfully handling divergent requirements by conforming to SRP. We’ve made our systems extensible by unleashing the power of OCP. Everything at WooMinus seems calm. But your life couldn’t be further from that. What happened?! What does this have to do with Liskov Substitution Principle? Read on to see how it will bring peace to your life.

King Benedictus And Real Politics

It’s late in the evening. Your wife asked you to go to the shop and buy a liter of milk and, if there are eggs, to get ten. So, you’re coming back carrying 10 liters of milk, the night appears so peaceful. Suddenly, a black van drives by and stops. Two big guys in hoodies punch you and force you in the car. It’s all black. A strangely familiar voice begins to whisper…

Listen carefully. I am King Benedictus. I hired a team of programmers known as the Black Dragons to write a system to control the goverment. Those suckers claimed it’s open for extension and similar nonsense. Yet, when I asked another team to add support for bribing different kinds of politicians, things started to break unexpectedly. I want you to find the issue and resolve it. You’ve got one hour. You fix the problem on time, and I’ll let you go and make sure you’re safe. You don’t? Pow!

Racing Against Time

You take the laptop and begin to search for the issue. The most prominent domain class in the system is Politician:

This must be the class that the other team tried to extend in order to add new types of politicians! You click the button to get to the subclasses and…

That’s weird. A corruptible politician that wants more money than a regular one? Also, he changes his mind about something completely different. This is a smell. Let’s look at Politician ‘s clients!

Aha! That explains everything!

Liskov Substitution Principle

The example above is a classic violation of the Liskov Substitution Principle:

Functions that use references to base classes must be able to use objects of derived classes without knowing it.

The person implementing GovermentService reasonably assumed that it’s enough to pay a politician 2000 dollars to make him change his mind about topic X. By adding a new type of politician to the system, the developers made the assumption invalid without fixing the broken code.

LSP and Conditions

Another way to state LSP, which I personally find much more intuitive, uses the terms usually associated with Design By Contract:

…when redefining a routine [in a derivative], you may only replace its precondition by a weaker one, and its postcondition by a stronger one.

If you look at our example from this perspective, the LSP violation should be very clear:

  • a precondition of the dollars argument being at least 2000 has been replaced by a stronger one requiring 3000
  • a postcondition of the politician having changed his mind about X has been replaced by a weaker one of not changing mind about it

To support this with a code example, let’s take a look at a different extension of Politician:

This one is perfectly valid. Is it enough to pay the guy 2000? It is! Will he change his mind about X? He will! You get the point.

Restoring Your Inner Peace

Now, that you know the ins and outs of LSP, you’re ready to save your life. King Benedictus claims that the system worked before adding the new type of politician. It’s also true, that the corruptible politicians will change their mind about topic Y when paid at least 3000 dollars. Let’s make the solution take the best from the old and the new:

Here, the method works with the GovermentService implementation and keeps the new benefits in place. You’re safe!

One More Thing…

Before I let you go. If GovermentService didn’t exist or it used some smarter way of determining the bribe amount, the described problems wouldn’t surface and we probably would not talk about an LSP violation – we could talk about a potential one at most. This is a key characteristic of this principle – everything depends on client’s expectations.

You probably know JDK methods like Arrays.asList() and Collections.unmodifiableXxx() . If you want to be hypercorrect, these do not violate LSP, because interfaces are not types and the rule originally was about subtyping. But when you look at client’s expectations, they rarely expect a list that does not support modifying it’s contents. Therefore, unless it’s completely clear for everyone that the collection is not modifiable, opt for the more evident immutable collections from Guava.

About the Author Grzegorz Ziemoński

King of Tidy Java, nerd that thinks about producing perfect software all the time and proud owner of 2 cats.

follow me on: