Liskov’s Substitution Principle | SOLID as a Rock
Tags: cpp, programming, tutorial, codenewbie
Published: 2020-05-18 12:35:11

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?](http://www.vishalchovatiya.com/what-is-design-pattern/) 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. **L**iskov's **S**ubstitution **P**rinciple in C++ is the second principle in this series which I will discuss here.

> /!\\\: This article has been originally published on my [blog](http://www.vishalchovatiya.com/liskovs-substitution-principle-in-cpp-solid-as-a-rock/). If you are interested in receiving my latest articles, [please sign up to my newsletter](http://eepurl.com/gDNybv).

By the way, If you haven't gone through my previous articles on design principles, then below is the quick links:

1.  [**S**RP -- Single Responsibility Principle](http://www.vishalchovatiya.com/single-responsibility-principle-in-cpp-solid-as-a-rock/)
2.  [**O**CP -- Open/Closed Principle](http://www.vishalchovatiya.com/open-closed-principle-in-cpp-solid-as-a-rock/)
3.  [**L**SP -- Liskov Substitution Principle](http://www.vishalchovatiya.com/liskovs-substitution-principle-in-cpp-solid-as-a-rock/)
4.  [**I**SP -- Interface Segregation Principle](http://www.vishalchovatiya.com/interface-segregation-principle-in-cpp-solid-as-a-rock/)
5.  [**D**IP -- Dependency Inversion Principle](http://www.vishalchovatiya.com/dependency-inversion-principle-in-cpp-solid-as-a-rock/)

The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like `override`, `final`, `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](http://www.vishalchovatiya.com/part-3-all-about-virtual-keyword-in-c-how-virtual-destructor-works/), constructor, [copy constructor](http://www.vishalchovatiya.com/all-about-copy-constructor-in-cpp-with-example/), 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.

***Note:***

-   If you stumbled here directly, then I would suggest you go through [What is design pattern?](http://www.vishalchovatiya.com/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++](http://www.vishalchovatiya.com/21-new-features-of-modern-cpp-to-use-in-your-project/) 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.


Intent
------

> ***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 `Square` is 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 `Square` derive from `Rectangle`, then a `Square` should be usable anywhere you expect a `Rectangle`. This makes for some strange behaviour as follows:

```C++
struct Rectangle {
    Rectangle(const uint32_t width, const uint32_t height) : m_width{width}, m_height{height} {}

    uint32_t get_width() const { return m_width; }
    uint32_t get_height() const { return m_height; }

    virtual void set_width(const uint32_t width) { this->m_width = width; }
    virtual void set_height(const uint32_t height) { this->m_height = height; }

    uint32_t area() const { return m_width * m_height; }

protected:
    uint32_t m_width, m_height;
};

struct Square : Rectangle {
    Square(uint32_t size) : Rectangle(size, size) {}
    void set_width(const uint32_t width) override { this->m_width = m_height = width; }
    void set_height(const uint32_t height) override { this->m_height = m_width = height; }
};

void process(Rectangle &r) {
    uint32_t w = r.get_width();
    r.set_height(10);

    assert((w * 10) == r.area()); // Fails for Square <--------------------
}

int main() {
    Rectangle r{5, 5};
    process(r);
    Square s{5};
    process(s);
    return EXIT_SUCCESS;
}
```

-   As you can see above, we have violated Liskovs's Substitution Principle in the `void process(Rectangle &r)` function. Therefore `Square` is not a valid substitute of `Rectangle`.
-   If you see from the design perspective, the very idea of inheriting `Square` from `Rectangle` is not a good idea. Because `Square` does not have height & width, rather it has the size/length of sides.

Liskov's Substitution Principle Example
-----------------------------------------------------------

### Not so good

```C++
void process(Rectangle &r) {
    uint32_t w = r.get_width();
    r.set_height(10);

    if (dynamic_cast<Square *>(&r) != nullptr)
        assert((r.get_width() * r.get_width()) == r.area());
    else
        assert((w * 10) == r.area());
}
```

-   A common code smell that frequently indicates an LSP violation is the presence of [type checking](http://www.vishalchovatiya.com/cpp-type-casting-with-example-for-c-developers/) code within a code block that is polymorphic.
-   For instance, if you have a `std::for_each` loop over a collection of objects of type `Foo`, and within this loop, there is a check to see if `Foo` is in fact `Bar`(a subtype of `Foo`), then this is almost certainly an LSP violation. Rather you should ensure `Bar` is in all ways substitutable for `Foo`, there should be no need to include such a check.

### An OK way to do it

```C++
void process(Rectangle &r) {
    uint32_t w = r.get_width();
    r.set_height(10);

    if (r.is_square())
        assert((r.get_width() * r.get_width()) == r.area());
    else
        assert((w * 10) == r.area());
}
```



-   No need to create a separate class for `Square`. Instead, you can simply check for `bool` flag within the `Rectangle` class to validate `Square` property. Though not a recommended way.

### Use proper inheritance hierarchy

```C++
struct Shape {
    virtual uint32_t area() const = 0;
};

struct Rectangle : Shape {
    Rectangle(const uint32_t width, const uint32_t height) : m_width{width}, m_height{height} {}

    uint32_t get_width() const { return m_width; }
    uint32_t get_height() const { return m_height; }

    virtual void set_width(const uint32_t width) { this->m_width = width; }
    virtual void set_height(const uint32_t height) { this->m_height = height; }

    uint32_t area() const override { return m_width * m_height; }

private:
    uint32_t m_width, m_height;
};

struct Square : Shape {
    Square(uint32_t size) : m_size(size) {}
    void set_size(const uint32_t size) { this->m_size = size; }
    uint32_t area() const override { return m_size * m_size; }

private:
    uint32_t m_size;
};

void process(Shape &s) {
    // Use polymorphic behaviour only i.e. area()
}
```
### With Factory Pattern

-   Still, creation or change is needed to process `Shape`, then you should try to use [Virtual Constructor](http://www.vishalchovatiya.com/7-advanced-cpp-concepts-idiom-examples-you-should-know/#Virtual-Constructor) & [Virtual Copy Constructor](http://www.vishalchovatiya.com/prototype-design-pattern-in-modern-cpp/) i.e. [Factory Pattern](http://www.vishalchovatiya.com/factory-design-pattern-in-modern-cpp/).

```C++
struct ShapeFactory {
    static Shape CreateRectangle(uint32_t width, uint32_t height);
    static Shape CreateSquare(uint32_t size);
};
```

Benefits of Liskov's Substitution Principle
-------------------------------------------

### => Compatibility

-   It enables the binary compatibility between multiple releases & patches. In other words, It keeps the client code away from being impacted.

### => Type Safety

-   It's the easiest approach to handle type safety with inheritance, as types are not allowed to *vary* when inheriting.

### => Maintainability

-   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](http://www.vishalchovatiya.com/memory-layout-of-cpp-object/), 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](http://www.vishalchovatiya.com/inside-the-cpp-object-model/) & 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](http://www.vishalchovatiya.com/part-1-all-about-virtual-keyword-in-cpp-how-virtual-function-works-internally/) in plain English.

Closing Notes
-------------

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**](https://en.wikipedia.org/wiki/Barbara_Liskov) for such a useful principle.



[Have Any Suggestions, Query or Wants to Say `Hi`? Take the Pressure Off, You Are Just a Click Away.](http://www.vishalchovatiya.com/contact-2/)🖱️