Liskov Substitution Principle (LSP) is a software design principle which states that “derived class objects should be completely replaceable by their base classes“. This principle ensures that any derived class extending base class will not change their behaviour.
Before going ahead we should know why do we need software design principle and what is software design principle.
LSP Advantages:
- Since every derived class is ensuring to support base class functionality which makes interfaces/classes loosely coupled.
- Easily testable code as new classes doesn’t break older codes.
- Easy to maintain as code is loosely coupled.
- Better code organisation.
Let’s understand LSP with a simple example:
Consider the problem related to abstract class birds.
// Sample code. Syntax not verified
class Bird
{
public:
virtual void setWeight (int weight) = 0;
virtual void maxFlightHeight (int height) = 0;
};
class Ostrich: public Bird
{
public:
virtual void setWeight (int weight)
{
// Do normal stuff
}
virtual void maxFlightHeight (int height)
{
// Do Nothing as ostrich doesn't fly
}
};
class Parrot: public Bird
{
public:
virtual void setWeight (int weight)
{
// Do normal stuff
}
virtual void maxFlightHeight (int height)
{
// Do normal stuff
}
};
Above classes looks like doing the job but let’s consider a case where we have a function which takes a pointer to base class “Bird” and do some normal stuffs.
// Sample code. Syntax not verified
void DoSomeNormalStuff (Bird* b)
{
b -> setWeight (12);
b -> maxFlightHeight (567);
Assert.Equal (12, b -> getWeight ());
Assert.Equal (567, b -> getHeight ());
}
In this case, if we are passing class “Parrot” object then everything works fine but same function fails in case “Ostrich” class object is passed in the function. This is the violation of LSP principle which states that “derived class objects should be completely replaceable by its base class.“
To solve this LSP violation we need to redesign the classes according to their properties:
// Sample code. Syntax not verified
class Bird
{
public:
virtual void setWeight (int weight) = 0;
};
class FlyingBird
{
public:
virtual void maxFlightHeight (int height) = 0;
};
class Ostrich: public Bird
{
public:
virtual void setWeight (int weight)
{
// Do normal stuff
}
};
class Parrot: public FlyingBird
{
public:
virtual void setWeight (int weight)
{
// Do normal stuff
}
virtual void maxFlightHeight (int height)
{
// Do normal stuff
}
};
Let’s analyse above code from LSP prespective.
- In this case if we are using “Bird” as base class pointer then we can only validate weight of all the birds which are common in every bird . So, LSP principle holds true.
- If we need to use “maxFlightHeight” then we need to use base class “FlyingBird” as super class as this function is defined by “FlyingBird” abstract class. So, LSP principle holds true too.