DEV Community

Cover image for Liskov Substitution Principle
Matthew Lucas
Matthew Lucas

Posted on

Liskov Substitution Principle

“If for each object o1 of type S, there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T”

Ouch! That’s a bit of a mouthful. Let’s forget the computer science-y language for a moment and focus on the core point. The Liskov substitution principle is all about doing inheritance well.

It tells us not to create subtypes that, despite conforming correctly at a contract level, diverge wildly from the true semantics creating some nasty surprises in the behavior of our program in the process.

The usual example used (and I’ll do no different through sheer laziness) is that of the square/rectangle problem. To begin, let’s say we have a type, Rectangle, with two methods #setX and #setY to assign the lengths of the two dimensions appropriately — so far so good.

Now, what if we create a subclass for the special case of a Square in which X and Y must always be equal?

If we have a program that uses Rectangle, and then all of a sudden we sneak in the Square subtype, things could get a bit strange when we call #setX and it changes Y too (or vice versa). The subtype has to make sense in the shadow of its parent.

This isn’t limited to just inheritance but also makes sense in ensuring the robustness of any interface, be it an API, REST call, or something else.

Substitution of any of these for another should be seamless, going unnoticed by any caller. If the client has to hack in a workaround to handle different implementations then we’ve botched the point of having a contract.

Respecting this principle, however, can often be quite difficult. Many interfaces aren’t quite as abstract as they really should be, leaking some implementation detail.

This is especially true when it comes to the things you may not always really consider, like exceptions.

To avoid setting this trap yourself, try and keep your interface declarations as simple as you possibly can — other developers will thank you.

Originally published at Better Programming

Discussion (0)