1.6 SOLID Design Principles in Java
← Back to Chapter 1: OOPs Concepts
The SOLID acronym introduced by Robert C. Martin (Uncle Bob) defines 5 guiding principles for creating robust, flexible, and testable object-oriented software architectures.
1. Single Responsibility Principle (SRP)
"A class should have one, and only one, reason to change."
- Violation: A class handling invoice business math, database persistence, and PDF formatting.
- Refactoring:
public class InvoiceCalculator {public BigDecimal calculateTotal(Invoice inv) { ... }}public class InvoiceRepository {public void save(Invoice inv) { ... }}public class InvoicePdfRenderer {public byte[] render(Invoice inv) { ... }}
2. Open / Closed Principle (OCP)
"Software entities should be open for extension, but closed for modification."
Extend application features via polymorphism without rewriting battle-tested existing code:
public interface DiscountStrategy {
BigDecimal applyDiscount(BigDecimal amount);
}
public class VipDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal applyDiscount(BigDecimal amount) {
return amount.multiply(BigDecimal.valueOf(0.80)); // 20% off
}
}
3. Liskov Substitution Principle (LSP)
"Subtypes must be substitutable for their base types without altering program correctness."
- Violation: Inheriting
SquarefromRectangleand overridingsetWidth()to mutate both width and height, violating consumer expectations about rectangles. - Solution: Model shared concepts via a common geometry interface (
Shape) rather than inappropriate inheritance.
4. Interface Segregation Principle (ISP)
"Clients should not be forced to depend upon interfaces that they do not use."
Favor small, specialized role interfaces over large "fat" interfaces:
// Anti-pattern: Fat interface
// interface Worker { void work(); void eat(); void sleep(); }
// Refactored: Focused interfaces
public interface Workable {
void executeTask();
}
public interface Manageable {
void assignShift(Shift shift);
}
5. Dependency Inversion Principle (DIP)
"High-level modules should not depend on low-level modules. Both should depend on abstractions."
This principle is the cornerstone of the Spring Framework IoC:
// High-level service depends on abstraction
public class OrderService {
private final PaymentProcessor paymentProcessor;
// Dependency injected through constructor
public OrderService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void checkout(Order order) {
paymentProcessor.pay(order.getAmount());
}
}