Theory
Liskov Substitution Principle (LSP)
📋 Overview
The Liskov Substitution Principle (LSP) is the "L" in SOLID. It states that objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program. If a function works with a Bird class, it must work perfectly with an Ostrich or Penguin without checking if (bird instanceof Ostrich).
🏗️ Core Principles & Characteristics
- The Contract: A subclass must honor the behavioral contract of its parent. This includes:
- Pre-conditions: Cannot be strengthened in a subclass (you can't require more than the parent).
- Post-conditions: Cannot be weakened in a subclass (you can't guarantee less than the parent).
- Signature Compatibility: Methods must accept the same (or broader) types and return the same (or narrower) types.
- Exception Safety: A subclass should not throw new, unexpected checked exceptions that the caller doesn't know how to handle.
⚖️ Trade-offs: Pros & Cons
- Pros:
- Polymorphism: Enables truly generic code that doesn't need to know concrete types.
- Maintainability: Changes in subclasses don't ripple through the entire codebase.
- Predictability: Reduces "surprises" during runtime.
- Cons:
- Inflexible Hierarchies: Can make deep inheritance trees difficult to design.
- Boilerplate: Often requires splitting classes into multiple interfaces to satisfy specific behaviors (e.g.,
Flyable,Swimmable).
🌍 Real-World Implementation
- Java Collections: You can swap an
ArrayListfor aLinkedListin any method accepting aList, and the application logic will remain correct (though performance may change). - Database Drivers: An application written for a generic
SQLDrivershould work withPostgreSQLDriverorMySQLDriverwithout modification, provided they adhere to the driver interface. - Payment Gateways: A
PaymentProcessorbase class can be substituted byStripeProcessororPayPalProcessorseamlessly.
💡 Interview "Gotchas" & Tips
- The "Classic" Rectangle-Square Problem: This is the #1 interview question. A
Squareis aRectanglemathematically, but if aRectanglehas asetHeight(h)andsetWidth(w)method, aSquareviolates LSP because setting the height will unexpectedly change the width. Solution: Use composition or separate them into aShapeinterface. - "Ask for Forgiveness" (Exceptions): If you find yourself throwing
UnsupportedOperationExceptionin a subclass (e.g.,Ostrich.fly()), you are almost certainly violating LSP. - Type Checking: If you see
if (obj instanceof SubClass)in your logic, it's a "code smell" indicating that your abstraction isn't substitutable.
📐 Suggested Architecture Primitives
- Interface Segregation: Break down fat classes into small, behavioral interfaces (e.g.,
Reader,Writer). - Composition over Inheritance: Instead of
Ostrich extends Bird, use aBirdclass that has aMovementStrategy(which could beWalkingStrategyorFlyingStrategy). - Design by Contract (DbC): Using tools or comments to strictly define what a method requires and what it guarantees.
Canvas