Understanding SOLID in C++ : Liskov Substitution principle
This is the 3rd part of the series “Understanding SOLID in C++”. In this article, we will talk about the “L” in SOLID i.e.?Liskov Substitution Principle.
In this series, we have already discussed about the?Single Responsibility Principle and Open-Closed Principle. If you have not gone through this, you can see from the below
Liskov Substitution Principle
Liskov Substitution Principle states that “Objects of superclass can be should be replaceable with objects of subclass without altering the correctness of program.”
In Simple terms, it means that a subclass should be able to be used wherever superclass is expected, and it should introduce the error or break the program.
In another words, You are expected that you should not reduce the number of functionality in the subclass than superclass.
Lets take an example to understand the above with a Flying Bird Example.
Flying Bird?Example
Suppose we have to design a bird hierarchy in which we need to implement birds like Penguin, Crow, etc.
class Bird {
public:
virtual void fly() = 0;
};
class Crow : public Bird {
public:
void fly() override {
cout << "Crow is flying" << endl;
}
};
class Penguin : public Bird {
public:
void fly() override {
throw runtime_error("Penguins can't fly");
}
};
In the above example, we have a class called Bird which is a abstract class having a pure virtual function called fly()?
We also have two subclasses Crow and Penguin that is inheriting Bird class. Since Penguins can’t fly the method fly() is throwing runtime_error.?
Problem in above?approach
Now let’s say we have a function that takes a Bird object and calls its fly() function.
void makeBirdFly(const Bird& bird) {
bird.fly();
}
If we call this method with Crow object, it will work as expected but if we call this method with Penguin object, it will throw a run time error. This violates the Liskov Substitution Principle because substituting an Penguin for a Bird results in unexpected behavior.
Here we are also reducing one functionality from the subclass Penguin.
领英推荐
There are many disadvantage if we don’t follow this principle
Solution to?above
To fix this violation, we can use more appropriate hierarchy.
We can have a intermediate superclass called FlyingBird that represents the birds that can fly.?
class Bird {
public:
virtual bool canFly() = 0;
};
class FlyingBird {
public:
bool canFly() override {
return true;
}
virtual void fly() = 0;
};
class Crow : public FlyingBird {
public:
void fly() override {
cout << "Crow is flying" << endl;
}
};
class Penguin : public Bird {
public:
void canFly() override {
return false;
}
};
Now let’s update our makeBirdFly() function to use this new hierarchy.
void makeBirdFly(const FlyingBird& bird) {
bird.fly();
}
Now, if we call makeBirdFly() with Crow object, it will work as expected. If we try to call it with Penguin object, it will give us Compile time error since Penguin does not inherit from FlyingBird.
This ensures that only a bird that can fly can be passed to makeBirdFly() function, which prevents from giving unexpected behavior, which maintains the Liskov Substitution Principle.
It is always recommended keeping your base Abstractions as simple and minimal as possible, making it easy to extend by the subclasses.
I hope we have understand the third principle of SOLID i.e. Liskov Substitution Principle using the examples in C++.
We will go through the remaining principles in our next article. Hope you learned somethings through this. See you in next article.?
We will cover the remaining principles in upcoming articles. We hope that you found the information provided thus far to be informative and valuable. Thank you for reading and we look forward to seeing you in the next article.
Subscribe to my newsletter for upcoming articles. I am targeting to cover low level design topics with case studies and the implementation and examples will be in C++.
Happy Coding!?
TIP: Implement these principles in C++ with some examples, its very easy to understand the applications.
Senior Principal Engineer Embedded Software at Northrop Grumman
1 年Hi Abhishek, Excellent example - thank you! I plan to share this with my team. I did have one question though, regarding your solution. Shouldn't the declaration for FlyingBird be: class FlyingBird : public Bird Otherwise, it is not clear what the canFly function is overriding from a base class, right? Thanks! -Robert
MS, BE |Professional Project Management by Google |CSM | Ex-Graphics Senior Engineer at Stryker R&D | Visualisation 3D & 2D | C++ | Medical devices
1 年Hello Abhishek, well written though this "In Simple terms, it means that a subclass should be able to be used wherever superclass is expected, and it should introduce the error or break the program." seams not right I think It should not introduce any error.
SWE @ISRO??| NavIC | Striker@ISRO Football Team
1 年True ! using instancof checks to invoke a function on inherited objects is dreadful at times !
Sales Associate at American Airlines
1 年Thanks for sharing
SDE @Amazon | GSoC @RedHat | Open Source and Coding Mentor |Ex @Nagarro|Ex @Coding Blocks|System Design Content Creator|20k+ linkedin followers|3 million views|open for collaborations
1 年????