The Liskov Substitution Principle (LSP)

A simple introduction to the Liskov Substitution Principle, a design guideline that helps us with inheritance.

Introduction

The LSP was first described by Barbara Liskov in a 1987 conference address entitled Data Abstraction and Hierarchy.

Despite the fancy name, it is actually quite a simple idea.

One way to explain it is this:

If A is a type of B, then everything we can say about B is also true of A.

Which means that:

If S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without breaking anything.

Examples

A Snake is an Animal

If Snake is a subtype of Animal then you can replace Animal with Snake and nothing should break.

In other words, anything that is true of Animal should also be true of Snake.

A Programmer is a Human

If we model a Programmer as a Type of Human, then (within the domain of our program) everything we can say about Human can also be said about Programmer.

To say this another way, we can take any instance of Human and replace it with a Programmer.

Challenges

The challenge here is that we intuitively model like this. As a result, we tend not to think about it…. and so occasionally we make a mistake.

Code Examples

For example, imagine a program that models Square as a sub-type of Rectangle:

Bad Design

A programmer has defined a Rectangle class as follows:

Later, someone else adds the Square class to the application. In this new code, Square is implemented as a sub-class of Rectangle:

At first sight, this seems reasonable; after all, every school child knows that a Square is just a special kind of Rectangle. Unfortunately for the second programmer, however, there are circumstances when you can’t substitute a Square for a Rectangle.

Consider a (single-threaded) client of the Rectangle class that has a method that does this:

  1. Set the value of Height to 200
  2. Set the value of Width to 400
  3. Read back the value of Height

The result is obviously 200. This is the value expected, because changing the Width of a rectangle normally has no effect on its height.

Now consider what would happen if the same method is passed a Square. This is reasonable, because we have said that a Square is a Rectangle:

  1. Set the value of Height to 200
  2. Set the value of Width to 400
  3. Read back the value of Height

Now the result is 400! Obviously, this is a different result from the same code.

We can see, therefore, that the second programmer violated the LSP, in that their specialist form of Rectangle doesn’t behave like a rectangle in all circumstances.

Of course, the second programmer could argue that it is obvious that changing the Width of a Square would obviously change its Height. However, other classes that use the Rectangle class have no special knowledge about Squares. They expect to be passed Rectangles, so they have every right to expect that the the things passed will behave like Rectangle… not like Squares.

The issues caused by violating LSP often manifest themselves in a different part of the code from the one that has changed. In this case, the change was to add the Square class as a sub-class of Rectangle, but the issue came to light in a client of the Rectangle class. In complex systems, issues like this can easily go unnoticed, and can very hard to debug, and even harder to fix.

Implementing LSP

It is important to think carefully about hierarchies of classes in your application.

Some ways to avoid violations of LSP include:

  • Avoid inheritance altogether – favour composition over inheritance
  • Keep class hierarchies shallow

See also:

Summary

If a Snake is a type of Animal, then anything we can say about an Animal is also something we can also say about a Snake.

Violations of LSP:

  • Can be difficult to spot, especially because we generally don’t think about LSP because it is “obvious”
  • Can result in subtle bugs in code
  • Can cause bugs that manifest themselves in different parts of the code than the ones that have changed

Leave a Reply

Your email address will not be published. Required fields are marked *