OOP · SOLID Principles
Liskov Substitution Principle (LSP)
Adding new if-else conditions whenever requirements change.
Common Mistakes
- Adding new if-else conditions whenever requirements change.
- Frequently modifying stable business classes.
- Hard-coding business rules inside one class.
- Violating OCP by editing tested code for every new feature.
- Ignoring abstraction and relying on concrete implementations.
Real-World Problem
Imagine you're developing a Payment Processing System. Initially, the application supports Credit Card Payments, and all payment classes inherit from a common Payment class. Later, the business introduces UPI, Net Banking, and Wallet Payments. Since all payment methods behave similarly, developers easily add new subclasses.
After some time, a new payment type called Cash on Delivery (COD) is introduced. Unlike other payment methods, COD does not process payments immediately. Developers still make it inherit from the same Payment class, but its behavior differs significantly from the parent.
Although the application compiles successfully, parts of the system that expect every payment to process immediately begin to fail unexpectedly.
Problem The application assumes that every subclass of Payment behaves exactly like the parent class. However, the CashOnDelivery class changes this expected behavior by throwing exceptions or leaving methods unimplemented.
As a result, code that works correctly with the parent class no longer works correctly when a subclass is substituted. This breaks polymorphism and makes the application unreliable.
Pain Points
- Parent class behavior is not preserved.
- Existing code breaks when subclasses are used.
- Runtime exceptions occur unexpectedly.
- Polymorphism becomes unreliable.
- Maintenance becomes more difficult.
Solution
The Liskov Substitution Principle states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program.
Instead of forcing unrelated behaviors into inheritance, only classes that truly satisfy the parent's contract should extend it. Every subclass should honor the behavior promised by the parent
Java Program 1 – LSP Violation

Java Program 2 – LSP Applied

Benefits
- Ensures safe use of inheritance.
- Makes polymorphism reliable.
- Reduces unexpected runtime errors.
- Improves maintainability.

Subtypes must be usable anywhere their base type is, without surprising the caller.
// BAD: Square breaks Rectangle's contract (setWidth changes height too)
// GOOD: model the real hierarchy so substitution is safe
interface Shape { int area(); }
class Rectangle implements Shape {
int w, h;
Rectangle(int w, int h) { this.w = w; this.h = h; }
public int area() { return w * h; }
}
class Square implements Shape {
int side;
Square(int side) { this.side = side; }
public int area() { return side * side; }
}
// Any Shape can replace another; none violates expectations.