Liskov Substitution Principle and Composition Over Inheritance

Tram Ho

The Liskov Substitution Principle (LSP) is one of the important design principles in object-oriented programming. This principle is named after Barbara Liskov, who introduced the concept in a 1987 paper. The idea behind LSP is that the object of a superclass can be replaced with the object of a subclass that is does not affect the correctness of the program. In other words, if you have a piece of code that works with a superclass, you can replace that superclass with any of its subclasses and the program will still work properly.

To explain this principle with an example, imagine we are building a program to simulate different animals. We start with an Animal class that has some basic properties and methods that all animals have in common:

Let’s say we want to create a subclass of Animal for a specific type of animal, for example Cat. We can do this by inheritance:

In this case, Cat inherits all the properties and methods of Animal, but we have added a new method specific to cat. This is a fairly simple example of inheritance, but it can actually lead to problems with the Liskov Substitution Principle.

For example, imagine we have a piece of code that works with an Animal object for feeding and sleeping:

We can call this function with an instance of Animal or any of its subclasses, including Cat:

Everything is easy but now we add another subclass of Animal, like RobotAnimal:

In this case, RobotAnimal is still a subclass of Animal, but we can see it behave differently in some important points. For example, instead of sleeping, it goes into standby mode and recharges the battery. If we try to call feedAndSleep with a RobotAnimal, it still works (because RobotAnimal inherits the eat method from Animal), but the sleep method won’t work properly:

This causes problems because the feedAndSleep function assumes that any Animal instance will behave in a certain way while it is sleeping. By breaking this assumption with the RobotAnimal class, we violate the Liskov Substitution Principle.

So how to fix this problem? One way is to use “Composition Over Inheritance”. Instead of creating a new subclass of Animal for each type of animal, we can create a separate class for each behavior we want to add. For example, we can create a SleepBehavior class:

We then use this class to add sleep behavior to our Animal and RobotAnimal classes:

Now, instead of using inheritance to add behavior, we are using Composition – creating separate classes that can be combined to create different types of objects. When we create an instance of Animal or RobotAnimal, we also create an instance of SleepBehavior or RobotSleepBehavior respectively. When we call sleep method on these objects, it will call sleep behavior object’s sleep method.

With this method, we can still use the feedAndSleep function, but it works with any object that has a sleep behavior, regardless of its superclass:

By using Composition Over Inheritance, we have avoided violating the Liskov Substitution Principle. We can add new behaviors to our objects by creating new classes and combining them differently, without worrying about the behavior of other objects in the hierarchy. This results in more flexible, modular and maintainable code.

Source: https://cuongvomanh.github.io/Liskov-Substitution-Principle-with-an-explanation-using-Composition-Over-Inheritance

Share the news now

Source : Viblo