SkillByAIOpen interactive version →

Lesson 16 / 25

Single Responsibility and Open/Closed

Apply SRP and OCP to keep classes focused and extensible.

Two principles about change

SOLID is an acronym popularised by Robert C. Martin for five object-oriented design principles. The Single Responsibility Principle (SRP): a class should have one reason to change, meaning it serves one actor or concern. An Invoice class that calculates totals, formats PDFs and sends emails has three reasons to change (tax rules, layout, mail provider); splitting it into InvoiceCalculator, InvoicePdfRenderer and InvoiceMailer keeps each change local. The Open/Closed Principle (OCP), from Bertrand Meyer: software entities should be open for extension but closed for modification. Adding a new payment method or discount type should mean adding a new class that implements an interface, not editing a growing if/else or switch in existing, tested code. Apply OCP where variation is likely; designing extension points for everything adds complexity without benefit.

One reason to change each

A class doing many jobs is split into focused classes, each changing for one reason.

Figure 6.1 — Splitting responsibilities and extending through new classes.

Open/closed with a discount strategy

New discount types are new classes; Checkout never changes.

// before: every new discount edits this method
long discountFor(Order o, String type) {
    if (type.equals("FESTIVE")) return o.totalPaise() / 10;
    else if (type.equals("STUDENT")) return 5000;
    else if (type.equals("FIRST_ORDER")) return Math.min(o.totalPaise() / 5, 20000);
    return 0;
}

// after: closed for modification, open for extension
public interface Discount {
    long amountPaise(Order order);
}
public final class FestiveDiscount implements Discount {
    public long amountPaise(Order o) { return o.totalPaise() / 10; }
}
public final class StudentDiscount implements Discount {
    public long amountPaise(Order o) { return 5000; }
}

public final class Checkout {
    public long payable(Order order, Discount discount) {
        return order.totalPaise() - discount.amountPaise(order);
    }
}

SRP is about reasons to change, not number of methods

A class with ten small methods can satisfy SRP; a class with two methods for unrelated concerns may not. Ask who would request a change to this class, and whether they are different people or teams.

Quick check: According to the Open/Closed Principle, how should a new payment method usually be added?

  • Edit a large switch statement in existing code
  • Copy the whole payment module
  • Add a new class implementing an existing interface
  • Make all fields public
Answer

Add a new class implementing an existing interface — OCP favours extension through new implementations rather than modifying tested code.