SOLID Principles • Liskov Substitution PrincipleMedium⏱️ ~3 min
Applying LSP: Library Management System
Consider a library management system where different types of library items have different lending rules. LSP ensures that all items can be treated uniformly by the lending system.
Initial Design (Violates LSP)
LibraryItem
- title: String
- id: String
- id: String
+ checkOut(member): Result
+ checkIn(): Result
+ calculateLateFee(days): Money
+ checkIn(): Result
+ calculateLateFee(days): Money
▲
Book
+ checkOut(member): Result
+ calculateLateFee(days): Money
+ calculateLateFee(days): Money
ReferenceBook
+ checkOut(member): Result
+ calculateLateFee(days): Money
+ calculateLateFee(days): Money
Problem: ReferenceBook cannot be checked out (reference materials stay in library). The checkOut() method must throw an exception or return an error, violating the parent's contract. Client code expecting all LibraryItem objects to be lendable breaks.
function processCheckout(item: LibraryItem, member: Member) {
result = item.checkOut(member)
if result.isSuccess() {
// This assumption fails for ReferenceBook!
sendConfirmationEmail(member)
}
}
result = item.checkOut(member)
if result.isSuccess() {
// This assumption fails for ReferenceBook!
sendConfirmationEmail(member)
}
}
LSP-Compliant Solution
LibraryItem
- title: String
- id: String
- id: String
+ getDetails(): ItemDetails
+ isAvailable(): boolean
+ isAvailable(): boolean
▲
LendableItem
- dueDate: Date
+ checkOut(member): Result
+ checkIn(): Result
+ calculateLateFee(days): Money
+ checkIn(): Result
+ calculateLateFee(days): Money
ReferenceItem
- location: String
+ reserve(member): Result
+ getLocation(): String
+ getLocation(): String
▲ ▲
Book
DVD
Encyclopedia
Magazine
Key improvements:
LibraryItemcontains only operations common to all itemsLendableItemadds checkout capability for items that can leave the libraryReferenceItemadds reservation capability for in-library use only- All subtypes are substitutable within their hierarchy
Interview Tip: When designing hierarchies, start with the most general abstraction and add specialized interfaces for additional capabilities. This follows the Interface Segregation Principle (ISP) and naturally supports LSP by not forcing subclasses to implement irrelevant operations.
Client Code Benefits
function processLending(item: LendableItem, member: Member) {
// All LendableItem subtypes work correctly
result = item.checkOut(member)
if result.isSuccess() {
sendConfirmationEmail(member)
}
}
function searchCatalog(query: String): List<LibraryItem> {
// Can return mix of lendable and reference items
// Client handles based on actual type
}
// All LendableItem subtypes work correctly
result = item.checkOut(member)
if result.isSuccess() {
sendConfirmationEmail(member)
}
}
function searchCatalog(query: String): List<LibraryItem> {
// Can return mix of lendable and reference items
// Client handles based on actual type
}
💡 Key Takeaways
✓Separate lendable and non-lendable items into different hierarchies
✓Base class should only define operations that all subclasses can meaningfully implement
✓Use intermediate abstractions (LendableItem) to group related behaviors
✓Each hierarchy level maintains LSP by honoring all parent contracts
✓Client code can work with appropriate abstraction level without type checking
📌 Examples
1Library system with lendable books vs reference-only encyclopedias
2Separating concerns into LendableItem and ReferenceItem hierarchies