Single Responsibility in Java
One reason to change.
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
}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) { ... }
}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
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.
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.
Who changes?
Your order code is split into PricingPolicy, OrderRepository, ReceiptFormatter and OrderNotifier. Marketing wants a new email template. Which class changes?
PricingPolicyOrderNotifierReceiptFormatterAll 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 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
- 'One reason to change' means one concern, not one method
- Mixing logic, formatting and persistence is the classic smell
- 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.
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() { ... }
}- Report has too many methods
- Report has several reasons to change, owned by different teams
- Report should be an interface
- 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?
- Tax calculation and PDF rendering in one class
- Business rules and raw SQL in one class
- Input validation and email sending in one class
- 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.