Command Palette

Search for a command to run...

Level 2 · 25 min

SOLID Applied

SOLID principles applied specifically to OOP class design guide how to structure classes, hierarchies, and dependencies to achieve maintainability and extensibility.

SRP and OCP in Class Design

SRP guides cohesion: a class should change for only one reason. Measuring cohesion: all methods in the class use most of the fields. Low cohesion (methods use only 1-2 of 10 fields) signals the class should be split. OCP in class design: abstract base class or interface defines the extension point. Concrete subclasses add new behavior without modifying existing classes. The Template Method pattern is OCP applied to a class hierarchy. Martin's Clean Code (Chapter 13 - Clean Classes) ties SRP to testability directly: "Kent Beck's four rules of simple design drive us toward small, single responsibility classes. The resulting classes are easier to test, they can help us eliminate redundancies more easily, and they promote clear, intention-revealing names." Martin's heuristic for measuring SRP compliance: try to describe the class in 25 words or fewer without using 'and', 'or', 'but', or 'if'. Each conjunction reveals an additional responsibility.

LSP in Inheritance Design

LSP-compliant hierarchies: Rectangle and Square problem. If Rectangle has setWidth(w) and setHeight(h) operating independently, Square cannot honor this — Square.setWidth(5) must also set height=5. Solution: both implement Shape with area() — no shared mutable state semantics. The ISP principle: a FlyingBird and a SwimmingBird interface are better than a Bird interface with fly() and swim().

DIP in Class Design

DIP via constructor injection: high-level classes (OrderProcessor) receive their dependencies (PaymentGateway, InventoryService) via constructor — no new inside business logic. This enables testing: pass test doubles in unit tests, real implementations in production. Spring @Autowired injects at runtime. The class graph is inverted — details depend on abstractions, not the reverse.

Key Takeaways

  • High cohesion + low coupling = SOLID classes. Measure: do all methods use most fields?
  • LSP in hierarchies: subclass postconditions must be at least as strong as the base class guarantees.
  • DIP via constructor injection makes unit testing trivial — no static mocks, no PowerMock needed.

Code example

// SOLID-compliant class design
interface PaymentGateway { PaymentResult charge(Money amount); }
interface InventoryService { boolean reserve(List<OrderItem> items); }
interface OrderRepository { void save(Order order); }

// OrderProcessor: high cohesion, depends on abstractions
class OrderProcessor {
  private final PaymentGateway payment;     // DIP
  private final InventoryService inventory; // DIP
  private final OrderRepository orders;     // DIP

  OrderProcessor(PaymentGateway p, InventoryService i, OrderRepository o) {
    this.payment = p; this.inventory = i; this.orders = o;
  }

  OrderResult process(OrderRequest req) {
    if (!inventory.reserve(req.items())) return OrderResult.outOfStock();
    PaymentResult pr = payment.charge(req.totalAmount());
    if (!pr.success()) return OrderResult.paymentFailed(pr.reason());
    Order order = Order.create(req, pr.transactionId());
    orders.save(order);
    return OrderResult.success(order.id());
  }
}