The Liskov Substitution Principle (LSP)
In the previous article about SOLID, we talking about the Open close principle today we will talk about the Liskov substitution principle
What is the Liskov substitution principle?
Liskov Substitution Principle states the following: “in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)”. Simply said, any object of some class in an object-oriented program can be replaced by an object of a child class. In order to understand this principle better, we’ll make a small digression to briefly remind ourselves about the concept of inheritance and its properties, as well as subtyping, a form of polymorphism.
Disadvantages of violating the LSP?
Code that does not adhere to the LSP is tightly coupled and creates unnecessary entanglements. The classic example introduced in Agile Software Development: Principles, Patterns, and Practices illustrated the issues of having a Square inherit from a Rectangle. There are a number of other blogs and articles that discuss this specific issue in depth here, here, and here. The disadvantage for writing code that does not adhere to LSP is that when a subclass can not substitute its parent class there would have to be multiple conditional statements to determine the class or type to handle certain cases differently. If there are changes that are required then these changes would have to be applied in multiple places. Furthermore, the entanglement that is created can lead to unanticipated behaviors.
Code that adheres to the LSP is code that makes the right abstractions. Robert C. Martin (Uncle Bob) stated that “the representatives of things do not share the relationship of the things they represent.” In other words, the code we write is merely a representation of a particular concept, even though that concept has a particular hierarchical relationship to something else in the real world, it does not necessarily mean that the real-world relationship between would hold when translated into code. The take away for me is to formulate the abstractions based on the logical structure of the code and not fall into the trap of letting real-world relationships force their way into the design decisions of my application.
Now it is important to understand that principle with code examples, Consider you have a class called TrasportationDevice. this class implemented as shown below
Now Car Class will extend TrasportationDevice. this will mean all features in TrasportationDevice. will be in Car class like startEngine() function the implementation will be as shown below ..
There is no problem here, right? A car is definitely a transportation device, and here we can see that it overrides the startEngine() method of its superclass.
Now we will make another class called Bicycle this class will also take all features of Car class but now we have a problem that Bicycle can not be startEngine() functions as it not have an engine .. class will be as shown below
Everything isn’t going as planned now! Yes, a bicycle is a transportation device, however, it does not have an engine and hence, the method startEngine() cannot be implemented. l
These are the kinds of problems that violation of the Liskov Substitution Principle leads to, and they can most usually be recognized by a method that does nothing or even can’t be implemented.
The solution to these problems is a correct inheritance hierarchy, and in our case, we would solve the problem by differentiating classes of transportation devices with and without engines. Even though a bicycle is a transportation device, it doesn’t have an engine. In this example our definition of a transportation device is wrong. It should not have an engine.
We can refactor our TransportationDevice class as follows:
Now we can extend TransportationDevice for non-motorized devices.
And extend TransportationDevice for motorized devices. Here is more appropriate to add the Engine object.
Thus our Car class becomes more specialized while adhering to the Liskov Substitution Principle.
And our Bicycle class is also in compliance with the Liskov Substitution Principle.
Any feedback is welcome
Best regards: Anwar Samir