This is part three of a five part series about SOLID class design principles by Robert C. Martin. The SOLID principles focus on achieving code that is maintainable, robust, and reusable. In this post, I will discuss the Liskov Substitution Principle.

The Liskov Substitution Principle (LSP): functions that use pointers to base classes must be able to use objects of derived classes without knowing it.

Liskov Substitution Principle by Derick Bailey

Image courtesy of Derick Bailey.

When first learning about object oriented programming, inheritance is usually described as an "is a" relationship. If a penguin "is a" bird, then the Penguin class should inherit from the Bird class. The "is a" technique of determining inheritance relationships is simple and useful, but occasionally results in bad use of inheritance.

The Liskov Substitution Principle is a way of ensuring that inheritance is used correctly.

Tom’s Penguin Problem

The classic example of the "is a" technique causing problems is the circle-elipse problem (a.k.a the rectangle-square problem). However, I’m going use penguins.

First, consider an application that shows birds flying around in patterns in the sky. There will be multiple types of birds, so the developer decides to use the Open Closed Principle to "close" the code to the addition of new types of birds. To do this, the following abstract Bird base class is created:

class Bird {
public:
	virtual void setLocation(double longitude, double latitude) = 0;
	virtual void setAltitude(double altitude) = 0;
	virtual void draw() = 0;
};

Version one of BirdsFlyingAroundApp is a huge success. Version two adds another 12 different types of birds with ease, and is also a success. Hooray for the Open Closed Principle. However, version three of the app is required to support penguins. The developer makes a new Penguin class that inherits from the Bird class, but there is a problem:

void Penguin::setAltitude(double altitude)
{
	//altitude can't be set because penguins can't fly
	//this function does nothing
}

If an override method does nothing or just throws an exception, then you’re probably violating the LSP.

When the app is run, all the flying patterns look wrong because the Penguin objects ignore the setAltitude method. The penguins are just flopping around on the ground. Even though the developer tried to follow the OCP, he failed. Existing code must be modified to accommodate the Penguin class.

While a penguin technically "is a" bird, the Bird class makes the assumption that all birds can fly. Because the Penguin subclass violates the flying assumption, it does not satisfy the Liskov Substitution Principle for the Bird superclass.

Why Violating The LSP is Bad

The whole point of using an abstract base class is so that, in the future, you can write a new subclass and insert it into existing, working, tested code. This is the essence of the Open Closed Principle. However, when the subclasses don’t adhere properly to the interface of the abstract base class, you have to go through the existing code and account for the special cases involving the delinquent subclasses. This is a blatant violation of the Open Closed Principle.

For example, take a look at this fragment of code:

//Solution 1: The wrong way to do it
void ArrangeBirdInPattern(Bird* aBird)
{
	Pengiun* aPenguin = dynamic_cast<Pengiun*>(aBird);
	if(aPenguin)
		ArrangeBirdOnGround(aPenguin);
	else
		ArrangeBirdInSky(aBird);
}

The LSP says that the code should work without knowing the actual class of the Bird object. What if you want to add another type of flightless bird, like an emu? Then you have to go through all your existing code and check if the Bird pointers are actually Emu pointers. You should be wrinkling your nose at the moment, because there is definitely a code smell in the air.

Two Possible Solutions

We want to be able to add the Penguin class without modifying existing code. This can be achieved by fixing the bad inheritance hierarchy so that it satisfies the LSP.

One not-so-great way of fixing the problem is to add a method to the Bird class named isFlightless. At least this way additional flightless bird classes can be added without violating the OCP. This would result in code like so:

//Solution 2: An OK way to do it
void ArrangeBirdInPattern(Bird* aBird)
{
	if(aBird->isFlightless())
		ArrangeBirdOnGround(aBird);
	else
		ArrangeBirdInSky(aBird);
}

This is really a band aid solution. It hasn’t fixed the underlying problem. It just provides a way to check whether the problem exists for a particular object.

A better solution would be to make sure flightless bird classes don’t inherit flying functionality from their superclasses. This could be done like so:

//Solution 3: Proper inheritance
class Bird {
public:
	virtual void draw() = 0;
	virtual void setLocation(double longitude, double latitude) = 0;
};

class FlightfulBird : public Bird {
public:
	virtual void setAltitude(double altitude) = 0;
};

I don’t think the English language has a word that means the opposite of "flightless", but let’s be Shakespearian and invent the word "flightful" to fill the position. In the above solution the Bird base class does not contain any flying functionality, and the FlightfulBird subclass adds that functionality. This allows some functions to be applied to both Bird and FlightfulBird objects; drawing for example. However, the Bird objects, which may be flightless, can not be shoved into functions that take FlightfulBird objects.

  • Pingback: SOLID Class Design: The Dependency Inversion Principle « Tom Dalling

  • Noemail

    Hi Tom,

    from a software architecture point of view I can fully agree to you. Subclassing the Bird for the FlightfulBird does make sense to group the flying ones alltogether. It is a reasonable choice both from a software design point of view and the semantically perspective.

    But in practice the downsides stay exactly the same. To implement an apropriate ArrangeBirdInPattern-method you need to approximately do the following:

    void ArrangeBirdInPattern(Bird* aBird)
    {
    FlightfulBird * aFlyingOne= dynamic_cast(aBird);
    if(aFlyingOne)
    ArrangeBirdInSky(aFlyingOne);
    else
    ArrangeBirdOnGround(aBird);

    }

    That looks pretty similar to the “blatant violation of the Open Closed Principle” earlier in this article. The difference is the use of an (abstract) superclass or interface instead of a single concrete subclass of Bird.

    Did I get something wrong or is the LSP rather an academic subtleness and a design faux pas that a practical problem?

    Besides in the Java Collection Framework the use of UnsupportedOperationException is pretty common (Collections.unmodifiableList()). The interface List itself even declares that such an Exception may be throws. Is this a violation of LSP?

    Best regards
    Andy

  • http://tomdalling.com/ Tom Dalling

    Yes, in that respect this Bird/FlightfulBird example isn’t great. If this were real software, I wouldn’t use subclassing for every different type of bird. I’d just have a single Bird class, with a “setCanFly” method on it. That would make the whole problem go away.

    When you say that your code snippet looks similar to the “Solution 1″ code and therefor violates the OCP, I’d say it’s actually more similar to the “Solution 2″ code which doesn’t violate the OCP. This is because when you make a new flightless bird class, you don’t have to modify the ArrangeBirdInPattern function. In “Solution 1″, you have to modify the function every time a new flightless bird class is created.

    Like most software development advice, the LSP is just a rule of thumb. You can think of an LSP violation as a kind of code smell. It indicates that there may be a better way to structure the code.

    As for the Java exceptions, I had a quick google (doesn’t that sound dirty) and found this:

    class: java.nio.ByteBuffer
    method: public ByteBuffer put(ByteBuffer src)
    throws: ReadOnlyBufferException (extends UnsupportedOperationException) – If this buffer is read-only

    If you have a subclass of ByteBuffer that is read-only, I would say that it does violate the LSP. If you can never call “put”, then it shouldn’t have a “put” method at all. You could get around this by having two interfaces like ReadableByteBuffer and WritableByteBuffer. Then you can choose not to implement WritableByteBuffer if the class is read-only.

  • http://tomdalling.com/ Tom Dalling

    Yes, in that respect this Bird/FlightfulBird example isn’t great. If this were real software, I wouldn’t use subclassing for every different type of bird. I’d just have a single Bird class, with a “setCanFly” method on it. That would make the whole problem go away.

    When you say that your code snippet looks similar to the “Solution 1″ code and therefor violates the OCP, I’d say it’s actually more similar to the “Solution 2″ code which doesn’t violate the OCP. This is because when you make a new flightless bird class, you don’t have to modify the ArrangeBirdInPattern function. In “Solution 1″, you have to modify the function every time a new flightless bird class is created.

    Like most software development advice, the LSP is just a rule of thumb. You can think of an LSP violation as a kind of code smell. It indicates that there may be a better way to structure the code.

    As for the Java exceptions, I had a quick google (doesn’t that sound dirty) and found this:

    class: java.nio.ByteBuffer
    method: public ByteBuffer put(ByteBuffer src)
    throws: ReadOnlyBufferException (extends UnsupportedOperationException) – If this buffer is read-only

    If you have a subclass of ByteBuffer that is read-only, I would say that it does violate the LSP. If you can never call “put”, then it shouldn’t have a “put” method at all. You could get around this by having two interfaces like ReadableByteBuffer and WritableByteBuffer. Then you can choose not to implement WritableByteBuffer if the class is read-only.

  • Efim Zabarsky

    Great article, thanks!

    My question, can i do next inheritance, that, in my opinion, not violates LSP?

    public interface IBird   
    {       
    int MovingDistance();   
    }   

    public abstract class IFlyingBird : IBird   
    {       
    public abstract int FlyingDistance();       
    public int MovingDistance()       
    {
                return FlyingDistance();
    }   
    }   

    public abstract class INotFlyingBird : IBird   
    {       
    public abstract int SteppingDistance(); 
    public int MovingDistance()       
    {           
    return SteppingDistance();     
    }   
    }   

    public class KingFisher : IFlyingBird   
    {       
    public override int FlyingDistance()       
    {           
    return 600;       
    }   
    }   

    public class Ostrich : INotFlyingBird   
    {       
    public override int SteppingDistance()       
    {           
    return 123;       
    }   
    }

    using:
    static void Main(string[] args)       
    {           
    IBird[] birds = new IBird[2];           
    birds[0] = new KingFisher();           
    birds[1] = new Ostrich();           
    DoFlyBirds(birds);       
    }       

    private static void DoFlyBirds(IBird[] birds)       
    {           
    foreach (IBird b in birds)           
    {               
    Console.WriteLine(“Bird {0} can overcome {1} kilometers”, b.GetType().Name,  b.MovingDistance());           
    }       
    }

    Thanks

  • http://tomdalling.com/ Tom Dalling

    I guess it doesn’t technically violate LSP. However, because there is no difference between flying and non-flying birds in your code, you can remove INotFlyingBird and IFlyingBird. Just inherit straight from IBird. Although, if you’re only subclassing to provide a new value for IBird::MovingDistance, you might as well have a single class called “Bird” with a setMovingDistance method on it.

  • Pingback: Why NSOrderedSet Doesn’t Inherit From NSSet — A Real‑life Example of the Liskov Substitution Principle « Tom Dalling

  • Pingback: Michael Tsai - Blog - Why NSOrderedSet Doesn’t Inherit From NSSet

  • JR

    Opposite for flightless in winged

  • Pingback: SOLID Class Design: The Dependency Inversion Principle « Tom Dalling Tom Dalling