Liskov Substitution principle states that in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of the original program.
In other words, methods that use pointers or references to the base class must be able to use objects of the derived class without knowing it. This principle is all about maintaining the hierarchy between classes and objects so that the inheritance and subclasses work properly.
Let’s consider a classic example involving shapes: rectangles and squares.
In geometry, a square is a subtype of a rectangle as it fulfills all the properties of a rectangle. However, in object-oriented programming, this relationship can lead to problems if we consider a square as a subtype of a rectangle.
Let’s see a simple program in pseudo-code:
class Rectangle {
setWidth(int width) {...}
setHeight(int height) {...}
getWidth() {...}
getHeight() {...}
getArea() {
return width * height;
}
}
class Square extends Rectangle {
setWidth(int width) {
this.width = width;
this.height = width;
}
setHeight(int height) {
this.width = height;
this.height = height;
}
}
In this example, we have a Rectangle
class with setWidth
and setHeight
methods. The Square
class extends the Rectangle
class and overrides the setWidth
and setHeight
methods, because for a square, the width is always equal to the height.
Now, let’s see what happens when we use these classes:
Rectangle r = new Square();
r.setWidth(5);
r.setHeight(4);
print(r.getArea());
In this case, we would expect the area to be 20 (5*4), as we’re seemingly dealing with a rectangle. But because our Rectangle
reference r
is actually a Square
, the output will be 16 (44), which is incorrect if we're expecting to work with a rectangle.
This is a violation of the Liskov Substitution Principle, because we’re unable to substitute a base type (Rectangle
) with a subtype (Square
) without altering the correctness of the program.
To adhere to LSP, Square
and Rectangle
should not be in a parent-child relationship. They should be separate entities or they should both inherit from a more generic parent class, such as Polygon
, and have their own separate properties and methods.
The Liskov Substitution Principle (LSP) is a crucial aspect of object-oriented design and programming because it helps ensure that a system remains easy to maintain and extend over time. Here are a few reasons why we require LSP:
- Subtype Requirement: LSP is necessary to ensure that a subtype can assume the place of its supertype without affecting the program’s correctness. This means that an object of a superclass should be able to refer to an object of a subclass without causing any issues.
- Code Reusability: LSP allows programmers to reuse code more effectively, leading to cleaner and more efficient code. If subclasses can be substituted for their base classes, it means that methods don’t need to be overridden unnecessarily in the subclasses.
- Code Maintainability: LSP helps in maintaining code by ensuring that changes in the superclass or the addition of new subclasses do not affect the existing program. If a new derived class is added, you don’t need to change the existing code.
- Improves Polymorphism: LSP is required for polymorphism to work correctly in object-oriented programming. Polymorphism allows objects of different types to be treated as objects of a parent type. If LSP is violated, the program may not correctly handle the different types.
- Prevents Unexpected Behavior: Violating LSP can lead to unexpected behavior and bugs in the application that are hard to track down. Following LSP helps prevent such issues.
In conclusion, adhering to the Liskov Substitution Principle makes your software design more robust and flexible to changes, and it increases its longevity.