Java

SOLID: LSP, ISP, and DIP

The remaining three SOLID principles - LSP, ISP, and DIP - govern how classes and interfaces relate to each other. These principles prevent the subtle bugs and architectural decay that emerge when inheritance and dependencies are misused. Master these, and you'll design systems that remain flexible for years.

πŸ“‹ At a Glance

AspectDetails
Principles CoveredLSP, ISP, DIP
Key ConceptContracts and abstractions
DetectionUnsupportedOperationException, fat interfaces
Primary PatternInterface extraction, dependency injection
Business Impact70% reduction in integration bugs

🎯 What You'll Learn

After reading this article, you will be able to:

  • Explain LSP with the Rectangle/Square example and real-world cases
  • Identify LSP violations through behavioral contract analysis
  • Detect fat interfaces and apply Interface Segregation
  • Distinguish DIP from DI (they're not the same!)
  • Design abstractions that don't leak implementation details
  • Apply these principles in Spring Boot applications

πŸ”₯ Production Story: The Substitution That Broke Production

An e-commerce platform had a class hierarchy for product inventory:

JAVA(31 lines)
Code
Loading syntax highlighter...
The disaster: Order processing code treated all products uniformly:
JAVA(7 lines)
Code
Loading syntax highlighter...
The fallout:
  • 2,340 orders failed during Black Friday
  • $180,000 in lost sales
  • Customer trust damaged
  • 3-day emergency fix
The root cause: LSP violation. BundleProduct was not a valid substitute for Product because it couldn't honor the setQuantity() contract.
The fix:
JAVA(24 lines)
Code
Loading syntax highlighter...

🧠 Mental Model: Contracts and Substitutability

TEXT(26 lines)
Code
Loading syntax highlighter...
The Rectangle/Square problem visualized:
TEXT(11 lines)
Code
Loading syntax highlighter...

πŸ”¬ Deep Dive

Part 1: Liskov Substitution Principle (LSP)

Definition: Objects of a superclass should be replaceable with objects of a subclass without breaking the program.
Formal rules:
  1. Preconditions cannot be strengthened - Subtype must accept everything the parent accepts
  2. Postconditions cannot be weakened - Subtype must guarantee at least what parent guarantees
  3. Invariants must be preserved - If parent says "X is always true", subtype must maintain X
  4. History constraint - Subtype cannot allow state changes parent would prohibit

Detecting LSP Violations

Red flags in code:
JAVA(32 lines)
Code
Loading syntax highlighter...

Fixing LSP Violations

Strategy 1: Extract common interface
JAVA(33 lines)
Code
Loading syntax highlighter...

Strategy 2: Composition over inheritance
JAVA(42 lines)
Code
Loading syntax highlighter...

Strategy 3: Design by Contract (DbC)
JAVA(24 lines)
Code
Loading syntax highlighter...

Part 2: Interface Segregation Principle (ISP)

Definition: Clients should not be forced to depend on interfaces they don't use.
Translation: Many specific interfaces are better than one general-purpose interface.

Detecting ISP Violations

The "Fat Interface" smell:
JAVA(19 lines)
Code
Loading syntax highlighter...

Refactoring to ISP

Step 1: Identify client needs
TEXT(4 lines)
Code
Loading syntax highlighter...
Step 2: Create role interfaces
JAVA(35 lines)
Code
Loading syntax highlighter...

ISP in Real-World Java

JDK Example - Collections:
JAVA(23 lines)
Code
Loading syntax highlighter...
Spring Example - Repository interfaces:
JAVA(26 lines)
Code
Loading syntax highlighter...

Part 3: Dependency Inversion Principle (DIP)

Definition:
  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.
Common confusion: DIP β‰  Dependency Injection
TEXT(8 lines)
Code
Loading syntax highlighter...

The Abstraction Ownership Problem

Wrong approach: Low-level module owns the interface
TEXT(11 lines)
Code
Loading syntax highlighter...
Correct approach: High-level module owns the interface
TEXT(17 lines)
Code
Loading syntax highlighter...

Implementing DIP in Spring

JAVA(43 lines)
Code
Loading syntax highlighter...

Abstractions That Don't Leak

Leaky abstraction: Exposes implementation details
JAVA(10 lines)
Code
Loading syntax highlighter...
Clean abstraction: Hides implementation details
JAVA(12 lines)
Code
Loading syntax highlighter...

⚠️ Common Mistakes

Mistake 1: Marker Interfaces That Lie

JAVA(7 lines)
Code
Loading syntax highlighter...

Mistake 2: Interface for Everything

JAVA(16 lines)
Code
Loading syntax highlighter...

Mistake 3: Wrong Abstraction Level

JAVA(13 lines)
Code
Loading syntax highlighter...

Mistake 4: Circular Dependencies via Abstraction

JAVA(23 lines)
Code
Loading syntax highlighter...

πŸ› Debug This: The Square-Rectangle Paradox

A developer reports: "I have a failing test but the code looks perfectly correct. My Square extends Rectangle and my test says area should be 20, but I'm getting 25!"
JAVA(39 lines)
Code
Loading syntax highlighter...
Why does this "perfectly logical" code fail? Find the LSP violation!

βœ… Solution:
This is the classic Square-Rectangle Problem - the most famous LSP violation.
The issue: Square violates LSP because it doesn't behave like a Rectangle should. A client using Rectangle expects:
  • setWidth(4) followed by setHeight(5) β†’ area = 20
  • Width and height are independent
But Square couples them, breaking this expectation.
Why it fails:
  1. rect.setWidth(4) sets both width AND height to 4
  2. rect.setHeight(5) sets both width AND height to 5
  3. Result: 5 Γ— 5 = 25, not 4 Γ— 5 = 20
The lesson: Mathematical "IS-A" relationships (a square IS a rectangle) don't always translate to behavioral substitutability. LSP is about behavior, not taxonomy. The fix: don't use inheritance here; use composition or separate interfaces.

πŸ’» Exercises

Exercise 1: Fix the LSP Violation

⭐⭐ Difficulty: Medium | ⏱️ Time: 15 minutes

Task: Refactor this code to fix the LSP violation.
JAVA(21 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(24 lines)
Code
Loading syntax highlighter...

Exercise 2: Apply ISP

⭐⭐ Difficulty: Medium | ⏱️ Time: 20 minutes

Task: Split this fat interface into role-based interfaces.
JAVA(16 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(24 lines)
Code
Loading syntax highlighter...

Exercise 3: Apply DIP

⭐⭐⭐ Difficulty: Medium-Hard | ⏱️ Time: 20 minutes

Task: Refactor to follow DIP - make high-level module own the abstraction.
JAVA(20 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(27 lines)
Code
Loading syntax highlighter...

Exercise 4: Design Service Layer with DIP

⭐⭐⭐ Difficulty: Medium-Hard | ⏱️ Time: 25 minutes

Task: Design a payment service following DIP where the domain owns all abstractions.
JAVA(10 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(34 lines)
Code
Loading syntax highlighter...

Exercise 5: Complete SOLID Audit

⭐⭐⭐⭐ Difficulty: Hard | ⏱️ Time: 30 minutes

Task: Audit this class for ALL SOLID violations and refactor.
JAVA(24 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(54 lines)
Code
Loading syntax highlighter...

🎀 Senior-Level Interview Questions

Question #1: Explain LSP with a real example from your experience.

Strong answer:
In a previous project, we had a Cache interface with get() and put() methods. We created a ReadOnlyCache that threw exceptions on put().
The problem: Code like this broke:
JAVA(3 lines)
Code
Loading syntax highlighter...
The fix: We split into ReadableCache and WritableCache:
JAVA(7 lines)
Code
Loading syntax highlighter...
Now methods declare exactly what they need, and ReadOnlyCache implements only ReadableCache.

Question #2: How does ISP relate to microservices API design?

Strong answer:

ISP applies to APIs just like interfaces:

Fat API problem:
/api/orders - GET, POST, PUT, DELETE, GET analytics,
              GET reports, POST bulk, etc.

Mobile client only needs GET and POST but must handle entire contract.

ISP-inspired design:
/api/orders - Core CRUD (mobile client uses this)
/api/orders/analytics - Separate endpoint (dashboard uses this)
/api/orders/bulk - Separate endpoint (admin uses this)

Even better: GraphQL lets clients request exactly what they need.

Key insight: ISP is about not forcing clients to depend on things they don't use - applies to REST APIs, GraphQL schemas, SDK methods, everything.

Question #3: What's the difference between DIP and DI frameworks?

Strong answer:
DIP (principle): High-level modules should depend on abstractions, not concretions. Abstractions owned by high-level module.
DI framework (tool): Resolves and injects dependencies at runtime.
You can have DI without DIP:
JAVA(6 lines)
Code
Loading syntax highlighter...
You can have DIP without DI:
JAVA(7 lines)
Code
Loading syntax highlighter...
Best practice: Use both - DIP for design, DI framework for wiring.

Question #4: How do you test code that follows DIP?

Strong answer:

DIP makes testing trivial because you depend on abstractions:

JAVA(25 lines)
Code
Loading syntax highlighter...
Without DIP: Would need to mock concrete Stripe SDK, set up test credentials, etc.

Question #5: When is it OK to violate these principles?

Strong answer:
LSP violation acceptable:
  • Null Object pattern (intentionally different behavior)
  • Decorator that adds preconditions (with documentation)
  • Legacy wrapper gradually migrating behavior
ISP violation acceptable:
  • Small internal interfaces where splitting adds complexity
  • Performance-critical code where virtual dispatch matters
  • Marker interfaces (empty by design)
DIP violation acceptable:
  • Simple applications without swappable implementations
  • Performance-critical hot paths
  • Frameworks/libraries (they ARE the low-level module)
Key: Understand the principle, then make informed trade-offs.

Question #6: How do these principles interact with each other?

Strong answer:

They reinforce each other:

ISP enables LSP:
  • Small, focused interfaces have clearer contracts
  • Easier to implement fully (no UnsupportedOperationException)
DIP requires ISP:
  • High-level module defines focused interface it needs
  • Low-level implements only what's required
LSP enables DIP:
  • Substitutable implementations make abstraction meaningful
  • Can swap implementations confidently
Together they create:
  • Testable code (mock small interfaces)
  • Flexible architecture (swap implementations)
  • Stable high-level modules (insulated from changes)

πŸ“ Summary & Key Takeaways

Liskov Substitution Principle

AspectKey Point
DefinitionSubtypes must be substitutable for base types
RulesPreconditions, postconditions, invariants
DetectionUnsupportedOperationException, instanceof checks
FixExtract interface, composition, DbC

Interface Segregation Principle

AspectKey Point
DefinitionClients shouldn't depend on unused methods
DetectionEmpty implementations, UnsupportedOperationException
FixSplit into role-based interfaces
BenefitSmaller contracts, easier implementation

Dependency Inversion Principle

AspectKey Point
DefinitionDepend on abstractions, not concretions
Key insightHigh-level owns the abstraction
Not the same asDependency Injection (DI is technique, DIP is principle)
BenefitSwappable implementations, testability

SOLID Summary Table

PrincipleOne-liner
SRPOne reason to change
OCPExtend without modifying
LSPSubtypes honor contracts
ISPSmall, focused interfaces
DIPDepend on abstractions

🏁 Conclusion

LSP, ISP, and DIP complete the SOLID foundation. Together with SRP and OCP, they provide a framework for designing maintainable object-oriented systems.

Key insights:
  • LSP is about contracts, not just inheritance
  • ISP is about client needs, not interface size
  • DIP is about ownership, not just abstraction
Your next step: Continue to Part 4 (Code Smells - Complete Catalog) to learn how to detect problems that SOLID principles prevent.

πŸ“… Review Schedule for This Article

DayTaskTime
Day 1Review LSP behavioral substitutability concept5 min
Day 3Redo Exercise 1 (Fix LSP Violation - Bird/Ostrich)15 min
Day 7Answer interview questions without looking10 min
Day 14Redo Debug This (Square-Rectangle Paradox)10 min
Day 30Review ISP in your current project - are there fat interfaces?10 min

Next in series: [Part 4: Code Smells - Complete Catalog]