[L] The Liskov Substitution Principle
- March 8, 2023
- Reading time . 2 min
- Author: Yuniel Acosta
The Liskov substitution principle (LSP) is the third principle of the SOLID design principles. It states that subtypes should be substitutable for their base types. Specifically, objects of a superclass should be able to be replaced with objects of a subclass without altering the correctness of the program. This principle helps to ensure that objects of a subclass can be used interchangeably with objects of the superclass, without introducing any unexpected behavior.
An example of a bad practice that violates the Liskov Substitution Principle (LSP) is a class hierarchy where a subclass overrides a method in a way that changes the method’s contract.
open class Rectangle {
open var width: Int = 0
open var height: Int = 0
open fun area(): Int {
return width * height
}
}
class Square : Rectangle() {
override var width: Int
get() = super.width
set(value) {
super.width = value
super.height = value
}
override var height: Int
get() = super.height
set(value) {
super.width = value
super.height = value
}
}
In this example, the Square class is a subclass of the Rectangle class. However, the Square class overrides the width and height properties in a way that changes the class’s contract. Specifically, the Square class sets both the width and height properties to the same value, ensuring that a square is always a square. Nevertheless, this is not an actual square, since a square is a rectangle with equal width and height values. As a result, replacing an object of the Rectangle class with an object of the Square class may result in unexpected program behavior.
A better practice would be to create a new class Square that has its own properties and methods, while also extending the Rectangle class.
open class Rectangle {
open var width: Int = 0
open var height: Int = 0
open fun area(): Int {
return width * height
}
}
class Square : Rectangle() {
var side: Int = 0
override var width: Int
get() = side
set(value) {
side = value
}
override var height: Int
get() = side
set(value) {
side = value
}
override fun area(): Int {
return side * side
}
}
In conclusion, the Square class has its own properties and methods, making it interchangeable with the Rectangle class without introducing unexpected behavior. This adherence to the Liskov Substitution Principle results in code that is more maintainable and predictable.