Loading document...
Preparing annotations
Restricted Access
This content requires a premium membership to access. Upgrade to continue reading.
Chapter 7
The Liskov Substitution Principle
Barbara Liskov wrote the original definition of the Liskov Substitution Principle in 1988, though it wasn't called that at the time. It is as follows:
"If for each object o1 of type S there is an object o2 of type T, such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T."
Let's understand this principle through a practical example. Consider a program that works with rectangles. In a mathematical sense, a square is a rectangle. It has four sides and four right angles. But in software design, inheriting Square from Rectangle can lead to subtle bugs.
The Rectangle Problem
Imagine we have a Rectangle class with width and height properties. We write a function that calculates the area of a rectangle:
function calculateArea(r: Rectangle): number {
return r.getWidth() * r.getHeight();
}
Now suppose we create a Square class that inherits from Rectangle. A square, by definition, has equal width and height. So we might override the setters to maintain this invariant:
If we pass a Square object to the calculateArea function, it will work correctly. But what if we have code that sets width and height independently?
Your note
This is where inheritance breaks down. The Square cannot be substituted for Rectangle in all contexts, violating LSP.
Solution: Composition Over Inheritance
Rather than using inheritance, we should use composition. Both Rectangle and Square can implement a common Shape interface, but they don't inherit from each other:
interface Shape {
area(): number;
}
class Rectangle implements Shape {
constructor(
private width: number,
private height: number
) {}
area(): number {
return this.width * this.height;
}
}
class Square implements Shape {
constructor(private side: number) {}
area(): number {
return this.side * this.side;
}
}
This approach respects the Liskov Substitution Principle because Square is no longer claiming to be a Rectangle. They are both independent implementations of Shape, and either can be substituted wherever a Shape is expected.
Real-World Applications
In large software systems, violating LSP often leads to:
- Fragile base class problems
- Unexpected behavior in polymorphic code
- Tight coupling between parent and child classes
- Difficulties in testing and maintenance
The key insight is that inheritance should be used only when the child class truly is a subtype of the parent in all contexts, not just in name or structure.
Chapter 7 of 34 • 42% complete