SOLID Principles — The ‘L’

Enhancing code readability and maintainability by following SOLID principles

Logeshvar L
FAUN — Developer Community 🐾

--

“Clean code always looks like it was written by someone who cares”
― Michael Feathers

It is always about the kindness and the care we deploy for our fellow programmers. It is always up to us to decide if we want to lend a helping hand that makes everyone happy by writing easily understandable code and making their lives easier or make hell for them by writing code that never makes sense in one read.

I design and develop experiences that make people’s lives simple.

Liskov Substitution Principle

So far we’ve seen about the Single Responsibility Principle (‘S’) and Open Closed Principle (‘O’) in SOLID. Now it’s time we learn the next one. The next principle starting with ‘L’ is the Liskov Substitution Principle, abbreviated as LSP, named after Barbara Liskov who introduced this principle.

According to Barabara’s paper, the definition provided for LSP is

Let Φ(t) be a property provable about objects t of type T. Then Φ(s) should be true for objects s of type S where S is a subtype of T.

I think it’s complicated to understand in one go. At least I felt that when I read it first. But don’t worry, I’m here. Let’s break this down in simpler words.

Simplified LSP

We have two classes T and S, where S is a subclass of T. The base class has some methods which are also there in the subclass ( Φ(t) and Φ(s) ). If the base class objects in a program can be replaced with the subclass objects and the methods still work fine for new objects, then it is said to follow LSP.

Hence a more simplified definition would be:

Objects of a superclass shall be replaceable with objects of its subclasses without breaking the application (& behave in the same way).

As simple as that :

Consider you’re planning to buy a new car. Your old car is no longer suited for your use. But it was the best one at the time you bought it. It had the best air conditioning features, a great engine, airbags, lush car seats and whatnot. But it is no longer in the same state and it is uncomfortable for driving.

Hence you decide to buy a new model of it which was developed based on the old model with additional features. A normal consumer would expect the new model to have better features. At least retaining the old ones along with a better design, look etc.

But will you decide to buy it if the car seats are not as royal as they had looked in the old one?
Will you buy the new model if there are no safety measures, such as airbags are not present in the new one?

No, right? We would not accept a new model that cannot replace the old one. It should at least have the old features. But if it does not do that, then it is not acceptable and substitutable for the old car.

The same concept for classes is explained in a more complex manner by LSP. I hope you got the grip of it.

I’m reusing the strategy pattern based account class we had at the end of refactoring in OCP. We’re adding functionalities to our account class. An account in a bank should ideally be able to debit and credit money. So, I’ve added those method logics.

Now, we’ll extend this account class to have other account types in our bank such as Savings, Current, Fixed Deposit, etc.

Here, we can see the Saving account is fine. But there is a major issue with our Fixed Deposit account. It is overriding the debitAmount() method to give its own implementation saying it is an invalid operation, that the FD account cannot do debit transactions. It is logically correct. But programmatically, in a SOLID way it is very wrong as it violates the LSP principle.

Yep, now as it throws exceptions, we cannot replace the base class objects with the child class objects and expect the same behaviour. And as we add new exceptions to our child class, we also have to go and add that in the base class method signature. Hence touching the production code per se. And that is definitely not recommended. So what’s the solution? Let’s discuss that too.

Breaking the hierarchy

An easy and well-known way to overcome this problem is by simply breaking the hierarchy. Using interface based implementation is always better. At least in most cases. And also in Java where it doesn’t support multiple inheritance, using interfaces is useful.

This way of breaking the hierarchy is one of the ways of solving this issue. There are also other ways which I leave to you to explore.

How to identify LSP violations?

  • When a method is not implemented in the subclass.
  • An exception in the subclass for behaviour from the parent class which is not fulfilled.
  • The subclass method overrides the base class to give a new meaning.

Hence the principle clearly states that you can implement less restrictive validation rules, but you are not allowed to enforce stricter ones in your subclass. It can also be considered as an extension of SRP and OCP as it is properly splitting classes, giving the right responsibilities and keeping the classes precise. It also helps to decide the base class and subclass, whether they belong to the same hierarchy or not.

Thanks for reading this article! I hope you found value in this and it is useful for you. Feel free to share your thoughts in the comments!

Let’s make coding fun together! Follow for more content alike!

Join FAUN: Website 💻|Podcast 🎙️|Twitter 🐦|Facebook 👥|Instagram 📷|Facebook Group 🗣️|Linkedin Group 💬| Slack 📱|Cloud Native News 📰|More.

If this post was helpful, please click the clap 👏 button below a few times to show your support for the author 👇

--

--