Liskov’s Substitution Principle | SOLID as a Rock
Liskov’s Substitution Principle | SOLID as a Rock
Subtypes must be substitutable for their base types without altering the correctness of the program.
Join the DZone community and get the full member experience.Join For Free
So you know how to code in general, understand the object-oriented programming, learned C++, and completed at least one Software Development Course (if you're not there yet, these articles aren't for you). You can write software easily if you know at least one programming language, but is your code any good? Could it be done any better? Is it clean (and what on earth does that mean)? Is your architecture any good? Should you use a different one? What about Design Patterns? These were some of the questions I've had when I started, and answering them helped me to step up to a professional level. Which is why I have written these series SOLID as a Rock design principle. Liskov's Substitution Principle in C++ is the second principle in this series which I will discuss here.
By the way, If you haven't gone through my previous articles on design principles, then below is the quick links:
- SRP -- Single Responsibility Principle
- OCP -- Open/Closed Principle
- LSP -- Liskov Substitution Principle
- ISP -- Interface Segregation Principle
- DIP -- Dependency Inversion Principle
The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like
public(while inheritance) just to make code compact & consumable(most of the time) in single standard screen size. I also prefer
struct instead of
class just to save line by not writing "
public:" sometimes and also miss virtual destructor, constructor, copy constructor, prefix
std::, deleting dynamic memory, intentionally. I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using Jargons.
- If you stumbled here directly, then I would suggest you go through What is design pattern? first, even if it is trivial. I believe it will encourage you to explore more on this topic.
- All of this code you encounter in this series of articles are compiled using C++20(though I have used Modern C++ features up to C++17 in most cases). So if you don't have access to the latest compiler you can use https://wandbox.org/ which has preinstalled boost library as well.
Subtypes must be substitutable for their base types without altering the correctness of the program
- If I address this in the context of C++, this literally means that functions that use pointers/references to base classes must be able to substitute by its derived classes.
- The Liskov Substitution Principle revolves around ensuring that inheritance is used correctly.
Violating the Liskov's Substitution Principle
- A great & traditional example illustrating LSP was how sometimes something that sounds right in natural language doesn't quite work in code.
- In mathematics, a
Rectangle. Indeed it is a specialization of a rectangle. The "IS A" makes you want to model this with inheritance. However if in code you made
Rectangle, then a
Squareshould be usable anywhere you expect a
Rectangle. This makes for some strange behaviour as follows:
- As you can see above, we have violated Liskovs's Substitution Principle in the
void process(Rectangle &r)function. Therefore
Squareis not a valid substitute of
- If you see from the design perspective, the very idea of inheriting
Rectangleis not a good idea. Because
Squaredoes not have height & width, rather it has the size/length of sides.
Liskov's Substitution Principle Example
Not so good
- A common code smell that frequently indicates an LSP violation is the presence of type checking code within a code block that is polymorphic.
- For instance, if you have a
std::for_eachloop over a collection of objects of type
Foo, and within this loop, there is a check to see if
Foois in fact
Bar(a subtype of
Foo), then this is almost certainly an LSP violation. Rather you should ensure
Baris in all ways substitutable for
Foo, there should be no need to include such a check.
An OK way to do it
- No need to create a separate class for
Square. Instead, you can simply check for
boolflag within the
Rectangleclass to validate
Squareproperty. Though not a recommended way.
Use Proper Inheritance Hierarchy
With Factory Pattern
- Still, creation or change is needed to process
Shape, then you should try to use Virtual Constructor & Virtual Copy Constructor i.e. Factory Pattern.
Benefits of Liskov's Substitution Principle
- It enables the binary compatibility between multiple releases & patches. In other words, It keeps the client code away from being impacted.
- It's the easiest approach to handle type safety with inheritance, as types are not allowed to vary when inheriting.
- Code that adheres to LSP is loosely dependent on each other & encourages code reusability.
- Code that adheres to the LSP is code that makes the right abstractions.
Yardstick to Craft Liskov's Substitution Principle Friendly Software
- In most introductions to object-oriented programming, inheritance discussed as an "IS-A" relationship with the inherited object. However, this is necessary, but not sufficient. It is more appropriate to say that one object can be designed to inherit from another if it always has an "IS-SUBSTITUTABLE-FOR" relationship with the inherited object.
- The whole point of using an abstract base class is so that, in the future, you can write a new subclass & insert it into existing, working, tested code. A noble goal, but how to achieve it? First, start with decomposing your problem space --- domain. Second, express your contract/interfaces/virtual-methods in plain English.
Don't get me wrong, I like SOLID and the approaches it promotes. But it's just a shape of deeper principles lying in its foundation. The examples above made it clear what this principle is striving for i.e. loose coupling & ensuring correct inheritance.
Now, go out there and make your subclasses swappable, and thank Dr. Barbara Liskov for such a useful principle.
Published at DZone with permission of Vishal Chovatiya . See the original article here.
Opinions expressed by DZone contributors are their own.