Liskov Substitution Principle (LSP) – A Detailed Explanation
What is the Liskov Substitution Principle?
The Liskov Substitution Principle (LSP) is the third principle of the SOLID principles. It was introduced by Barbara Liskov in 1987 and states:
“Objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.”
In simpler terms, if class B is a subclass of class A, then we should be able to replace A with B without breaking the application.
Why is LSP Important?
- Ensures reliable inheritance and polymorphism.
- Promotes robust and predictable behavior in object-oriented design.
- Improves maintainability and extensibility of code.
Violation of LSP – An Example
Let’s take a look at an example that violates LSP:
public class Bird {
public void Fly() {
// flying logic
}
}
public class Ostrich extends Bird { @Override public void Fly() { throw new UnsupportedOperationException("Ostriches can't fly!"); } }
Problem: Ostrich is a subclass of Bird, but it can't fly. If we substitute Bird with Ostrich in a method expecting a Bird, the program will crash.
How to Fix the Violation
We should refactor the class hierarchy to avoid inappropriate inheritance:
public interface IBird {
void Eat();
}
public interface IFlyingBird extends IBird { void Fly(); }
public class Sparrow implements IFlyingBird { public void Eat() { // eating logic } public void Fly() { // flying logic } }
public class Ostrich implements IBird { public void Eat() { // eating logic } // No Fly method needed }
Result: Now we have a proper hierarchy. Flying birds implement IFlyingBird
, and non-flying birds implement IBird
, satisfying LSP.
Benefits of Applying LSP
- Reliable Polymorphism: Subtypes can be used without unexpected behavior.
- Improved Design: Forces correct abstraction and avoids incorrect inheritance.
- Better Testing: Easier to test individual components without side effects.
Conclusion
The Liskov Substitution Principle ensures that subclass objects behave in a manner consistent with expectations set by their base class. It is vital for designing robust, scalable, and understandable software systems.