Skip to main content

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 Square from Rectangle and overriding setWidth() 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());
}
}