Liskov’s Substitution Principle

Santiago Peña Mosquera
3 min readDec 22, 2020

--

It is a principle in object-oriented programming created by Barbara Liskov that states that:

Let phi(x) be a property provable about objects x of type T. Then phi(y) should be true for objects y of type S where S is a subtype of T.

In simpler words, if in our code we are using a class, and this class is extended, we have to be able to use any of the child classes without the program stopping working or presenting unexpected behaviors.

This principle contradicts the idea that in OOP all classes are a direct way to model reality, which is not always correct. This forces us to make sure that when we extend a class the behavior of the parent class is not altered.

Example

In the real world we can say that a square is a rectangle with equal width and height, however if we model a square in this way, the Liskov principle is not fulfilled.

For this example, we will first create a base class called Shape, which will implement a method called Area() since all geometric figures have an area, but in this case it will throw a NotImplementedException since each figure that inherits from it must implement its own area.

From the Shape class it inherits the Rectangle class, which as we already know is a geometric shape, which has as attributes height and width, which it uses to calculate its area, in its own implementation of the Area() method, apart from its own implementation of the ToString() method.

Following the logic that a square is a rectangle with width equal to height, we create a new class called Square, which inherits from Rectangle, this class has a size attribute which, when assigned, assigns its value to the height and width attributes, to calculate its area.

At first glance this code looks good, in fact if we create an instance of a square and assign it a size of 12, the area for this square will be 144 (12 * 12). But because the square class inherits from Rectangle, it also inherits its height and width attributes, so if we create a new square-type object we can assign it a different height and width, calculating the area as if it were that of a rectangle, and not a square as you can see in the following code.

Running this code produces the following output:

aSquare width: 12
aSquare height: 8
aSquare size: 0
aSquare area: 96
[Square] 0 / 0

Although the size is 0, if a height of 8 and a width of 12 are assigned, the square will have an area of ​​96 which is incorrect, so that square inherits from rectangle is not an ideal design option.

Solution

For this case, the solution is to make a more specific class hierarchy, taking the common features to a higher class (Shape) and leaving each class to model their differences.

Incorrect hierarchy:

Shape -> Rectangle -> Square

Correct hierarchy:

Shape -> Rectangle

Shape -> Square

Since a higher Shape class has already been modeled, Square must be modeled to inherit from this, and implement its own method of calculating area, as shown below.

When we breach the liskov principle?

The easiest way to detect that the liskov principle is being violated is when it is observed that, the classes that extend from others when implementing overridden methods, throw exceptions or null, where the parent classes did not, or when inheriting from one class, we are forced to throw exceptions or return null.

Conclusion

Liskov’s principle is something that should not be taken lightly, because it helps us to model the hierarchy between classes correctly, thus avoiding the errors that can occur when making use of inheritance in the Object-oriented programming, since generally the modeling is done taking the real world as a reference, following the same logic that we perceive in it and this is not always the case when it comes to code.

--

--