Understanding SOLID Principles: Liskov Substitution Principle
Tags: bestpractices, development
Published: 2017-11-23 14:36:23

---
title: "Understanding SOLID Principles: Liskov Substitution Principle"
published: true
description: Understand SOLID Principles in Practice Series
tags: bestpractices, development
---

![image](https://thepracticaldev.s3.amazonaws.com/i/8c53iit64r5b9qro3blk.jpg)

[**Read the Original Article here**](https://codeburst.io/understanding-solid-principles-liskov-substitution-principle-e7f35277d8d5)

*This is the 5th and last part of the series of understanding **SOLID** Principles where we explore what is **Liskov Substitution Principle** and why it helps with coding to abstractions rather than always coding to concrete implementations thus make code maintainable and reusable.*


As a small reminder, in **SOLID** there are five basic principles which help to create good (or solid) software architecture. SOLID is an acronym where:-

**S** stands for SRP (Single responsibility principle)
**O** stands for OCP (Open closed principle)
**L** stands for LSP (Liskov substitution principle)
**I** stand for ISP ( Interface segregation principle)
**D** stands for DIP ( Dependency inversion principle)

We’ve discussed [Dependency Inversion](https://codeburst.io/understanding-solid-principles-dependency-injection-d570c15560ab), [Single Responsibility](https://codeburst.io/understanding-solid-principles-single-responsibility-b7c7ec0bf80), [Open-Closed Principle](https://medium.com/r/?url=https%3A%2F%2Fcodeburst.io%2Funderstanding-solid-principles-open-closed-principle-e2b588b6491f) and [Interface Segregation Principle](https://codeburst.io/understanding-solid-principles-interface-segregation-principle-b2d57026cf6c) before.

Now we are going to address Liskov Substitution Principle.

## Liskov Substitution Principle

> Objects should be replaceable with instances of their subtypes without altering the correctness of that program.

**What it really means is that if you pass a subclass of an abstraction you need to make sure you don’t alter any behavior or state semantics of the parent abstraction.**

If you choose not to do that you will suffer:

* The class hierarchies would be a mess. Strange behavior would occur.
* Unit tests for the superclass would never succeed for the subclass. That will make your code difficult to test and verify.

Typically this would happen if you modify unrelated internal or private variables of the parent object that are used in other methods. *This is kind of a sneak attack on the object itself and it can happen anytime if your are not careful, not just though the subclasses.*

Let’s see an example of what do we mean about modifying behavior and how it affects the results. Let’s say we have a Basic `Store` Abstraction that stores messages in a memory data-structure up to a maximum store Limit. Now the clients using this store expect when they call the `retrieveMessages` to get all the messages back.

```typescript
interface Store {
    store(message: string);
    retrieveMessages(): string[];
}
const STORE_LIMIT = 5;
class BasicStore implements Store {
   protected stash: string[] = [];
   protected storeLimit: number = STORE_LIMIT;
  
   store(message: string) {
     if (this.storeLimit === this.stash.length) {
         this.makeMoreRoomForStore();
      }
      this.stash.push(message);
    }
  
    retrieveMessages(): string[] {
      return this.stash;
    }
    makeMoreRoomForStore(): void {
       this.storeLimit += 5;
    }
}
class RotatingStore extends BasicStore {
    makeMoreRoomForStore() {
        this.stash = this.stash.slice(1);
    }
}
```

Notice how the `RotatingStore` is doing something sneaky and weird, and how subtle it can modify the semantics of the `BasicStore`. It modifies the stash and destroys a message in the process to allow space for more messages. If we were trying to utilize a Storer abstraction but we ended up with the destructive `RotatingStore` we would have issues with our messages.

```typescript
const st: Store = new RotatingStore()// or from a factory
st.store("hello")
st.store("world")
st.store("how")
st.store("are")
st.store("you")
st.store("today")
st.store("sir?")
st.retrieveMessages() // Ooops some messages are gone
```

Some messages will be gone, violating the base requirement that messages should be available when retrieved.

In order to avoid those weird cases, it’s recommended to call public parent methods to get your results in the subclasses and not directly using the internal variables. **That way you are making sure the parent abstractions get to the required state without side effects or invalid state transitions.**

**Its also recommended keeping your base Abstractions as simple and minimal as possible,** making it easy to extend by the subclasses. There is no point in making a Fat Base class that makes it easy for the subclasses to override; thus introducing behavior changes.

It may or may not be feasible to do always but its a good idea to do **post-condition checking** in the sense of internal system health check in order to verify that the subclasses are not messing around some critical code paths. I haven’t seen this idea in practice though so I cannot give you an example.

**Code reviews on the other hand help very good.** While developing you may accidentally do more damage than you realise, so it’s important to get extra pairs of eyes on your code changes. It’s important to keep the design of the code consistent and pinpoint potential modifications that alter object hierarchies early and often.

**Lastly, did I mention before to consider using Composition instead of Inheritance? If not you should** [**_read that_**](https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose) **_._**

### Conclusion

As you have seen it take such a small effort to make things hairy and buggy when you develop software. Keeping those principles close at heart, understanding where they came from and what they try to solve makes your job easier and more reasonable. That’s not a small thing, instead, it should be part of your thinking process when you are on the task. Try to make your designs resilient against unfavorable modifications and sneaky throws


### References

* [ObjectMentor Article](http://www.objectmentor.com/resources/articles/lsp.pdf)
* [Wiki Page](https://en.wikipedia.org/wiki/Liskov_substitution_principle)

**Sadly this is the end of the Series. I hope you learned a lot and I helped you better understand those principles in practice. Stay put for more Series of articles as I have quite a lot of them in draft form.**

**If this post was helpful please share it and stay tuned on my other [articles](https://medium.com/@fanisdespoudis/). You can follow me on [GitHub](https://github.com/theodesp)  and [LinkedIn](https://www.linkedin.com/in/theofanis-despoudis-7bb30913/). If you have any ideas and improvements feel free to share them with me.**

Happy coding.

**If you would like to schedule a mentoring session visit my [Codementor Profile](https://www.codementor.io/theofanisdespoudis)**.