Software Twist – Adrian Tosca Blog

Understanding Liskov Substitution Principle

Posted in Patterns and Practices, Software Development by Adrian Tosca on 2009, June 14

The Liskov Substitution Principle, in its simplified, object oriented language way, says that:

Derived classes must be substitutable for their base classes.

The principle looks simple and obvious; at first look we are tempted to question that the opposite is true: can one make a class that is not substitutable for their base class? Technically speaking no. If one makes a derived class and the compiler does not complain everything should be fine. Or is it not?

Let’s see a simple example:

public class MusicPlayer {
    public virtual void Play(string fileName) {
        // reference implementation for playing
    }
}

// Optimized mp4 player
public class Mp4MusicPlayer : MusicPlayer {
    public override void Play(string fileName) {
        if (Path.GetExtension(fileName) != "mp4")
            throw new ArgumentException("can only play mp4");
        // optimized implementation for playing
    }
}

In the above example we have a reference implementation for a music player. The reference implementation would play all types of files but maybe not with the best performance or quality. An optimized player for a certain type of file, for example for a MP4 file type, is as far as the c# interface is concerned a specialized type of music player.

So what’s the problem? The above Mp4MusicPlayer class just violated the Liskov substitution principle. One cannot substitute MusicPlayer with Mp4MusicPlayer bacause the later works only with certain specific music files. Where MusicPlayer would work with a MP3 file, the Mp4MusicPlayer would throw an exception and the program will fail.

Where did this go wrong? As it seems, there is more to an interface that meets the eye. In our case the preconditions of the derived class are stronger than those of the base class and even if in c# the interface does not contain in it’s definition the preconditions you will have to keep them in mind when designing the inheritance tree.

But there is more than that. The above is only one way in which the Liskov substitution principle can be violated. In a more general terms is a problem of abstractions. When one defines a base class or an interface it actually defines an abstraction. And when one makes a derived class implicitly agrees to satisfy the same abstraction.

In the example above the MusicPlayer abstraction is “something that can play any music file”
But Mp4MusicPlayer abstraction is “something that can play MP4 music files”

The abstractions are different, and this is the root cause of the problem. When the abstractions are not appropriate the situation can get nasty.

Liskov substitution principle is all about abstractions

Liskov substitution principle is all about abstractions

There are multiple ways in which the abstraction can be broken. A wrong abstraction can be hacked in simple cases but eventually it will come back to byte you. We might be able to install an electronic device on the wooden horse in the right of the above image if we need the “horse” abstraction to neigh but we will never persuade it to eat a hand of hay.

Advertisements
Tagged with: ,

Comments Off on Understanding Liskov Substitution Principle