🏛️ Design Principles & Patterns · Advanced

Single Responsibility in Java

One reason to change.

🧩 The mysteryFinance tweaks one tax rule, and somehow the invoice PDF breaks. Nobody touched the PDF code... or did they?

One reason to change

The Single Responsibility Principle says a class should have one reason to change. A "reason" is a person or team who might ask for a change: finance owns the math, designers own the layout, DBAs own the schema. One class serving all three is one class that three teams keep editing.

class Report {
    Totals calculate() { ... } // finance
    String toHtml() { ... }    // web team
    void saveToDb() { ... }    // DBAs
}
🤔 Think first

Count the reasons

The DBAs rename a column, so someone edits saveToDb() in Report. Why should the finance team be nervous?

Think about it, then reveal the answer

Because their calculate() lives in the same class. The schema change forces edits, a rebuild and retesting of code finance relies on, and one slip can break their numbers. Three owners means three reasons to change.

Split by responsibility

The fix is to give each concern its own class. Now a new tax rule touches only Invoice, a new layout touches only InvoicePrinter, and a schema change touches only InvoiceRepo. Each class is small, focused and easy to test on its own.

class Invoice { Money total() { ... } }
class InvoicePrinter {
    String print(Invoice i) { ... }
}
class InvoiceRepo {
    void save(Invoice i) { ... }
}
⚠️ The trap

SRP is not "one method"

A class may have many methods as long as they serve one concern. A getter and a setter for the same field share one reason to change, so that's fine. The real smells mix unrelated concerns: tax math + PDF rendering, business rules + raw SQL, input validation + email sending.

Adding CSV export to Payroll

✗ Grows the class
class Payroll {
    Money salary(Employee e) { ... }
    String toCsv() { ... } // new concern
}

Export format is a new reason to change. A csvMode flag or extending a CsvWriter is the same mistake.

✓ Separate exporter
class PayrollCsvExporter {
    String export(Payroll p) { ... }
}

Salary rules stay untouched when the CSV layout changes, and a PDF exporter can be added the same way.

🔮 Predict it

Who changes?

Your order code is split into PricingPolicy, OrderRepository, ReceiptFormatter and OrderNotifier. Marketing wants a new email template. Which class changes?

  1. PricingPolicy
  2. OrderNotifier
  3. ReceiptFormatter
  4. All four
Show the answer

Only OrderNotifier, which owns sending confirmation emails. Discounts live in PricingPolicy, reading and writing orders in OrderRepository, and receipt text in ReceiptFormatter. One concern each, so one change touches one class.

💼 In the real world

In code reviews

"God classes" with thousands of lines are where merge conflicts and surprise regressions live, because every team edits them. Reviewers flag a class that mixes logic, formatting and persistence. Focused classes give smaller diffs, faster tests and safer reuse.

Key takeaways

  1. 'One reason to change' means one concern, not one method
  2. Mixing logic, formatting and persistence is the classic smell
  3. Focused classes are easier to test, reuse and review

💡 A chef keeps separate knives for bread and fish, so sharpening one never dulls the other.

🤯 Did you know?

Robert C. Martin described these design principles around 2000, and Michael Feathers later noticed their initials spelled SOLID. Martin has since rephrased SRP as being responsible to one actor.

Practice questions

Finance changes the report rules, the web team changes the HTML, and DBAs change the schema. What's the SRP problem with this class?

class Report {
    Totals calculate() { ... }
    String toHtml() { ... }
    void saveToDb() { ... }
}
  1. Report has too many methods
  2. Report has several reasons to change, owned by different teams
  3. Report should be an interface
  4. Report's methods should be static
Check your answer

Report has several reasons to change, owned by different teams. Each team's change forces edits to the same class, so one team's change can break another's feature. Splitting it into a calculator, a renderer and a repository isolates those changes.

Three of these are SRP violations. Which one is the odd one out?

  1. Tax calculation and PDF rendering in one class
  2. Business rules and raw SQL in one class
  3. Input validation and email sending in one class
  4. A getter and a setter for the same field
Check your answer

A getter and a setter for the same field. A getter and setter both manage the same piece of state, so they share one reason to change. The others combine unrelated concerns that change for different reasons.

Next: how do you add a 12th payment method without touching the 11 that already work?