All topics
SOLID principles
beginner

Single Responsibility Principle (SRP)

A class should have one, and only one, reason to change.

A class should have one job. That is the short version of SRP, and it is slightly wrong in a way that matters.

A restaurant kitchen: the chef cooks, the sommelier picks wine, the dishwasher cleans. One person doing all three serves customers slowly and badly.

Key Concepts

1
The real rule is about change: a class should have one reason to change. Put differently, it should answer to one group of people.
2
An example. You have a class called OrderReport. It does three things:
OrderReport
3
  calculateTotal()    the finance team decides these rules
  formatHtml()        the design team decides how it looks
  saveToDatabase()    the platform team decides where data goes
4
Three teams can each demand a change to this one file. A tax change, a redesign and a database migration all land in the same place, and each one risks breaking the other two.
5
Split it into three classes and each team edits its own.
6
Why "one job" is a poor test. Is "handle orders" one job? Is "check the delivery address on an order" one job? Both sound reasonable. The phrase gives you no way to decide, so people argue about it. "One reason to change" gives you a question you can actually answer: who asks for this to change?
7
A check you can run on real code. Look at the fields in the class, then at which methods use them. If half the methods use one set of fields and the other half use a completely different set, you have two classes sharing a name. Splitting them is usually obvious once you look.
8
You can also go too far. Some people split until every class has one method. Then reading a single piece of behaviour means opening fifteen files, and nothing is reusable. A long class that only one team has touched in two years is usually fine. A short class that three teams keep editing is not.
9
What the interviewer is probing.1. "What is SRP?" Probing: whether you learned the phrase or the idea. Stalls: "A class should do one thing." Moves up: one reason to change, one group it answers to — and why "one thing" cannot be tested.
10
2. "Is this class breaking it?" Probing: whether you can apply it. Stalls: counting the methods. Moves up: asking who can force a change, and checking whether the methods share fields.
11
3. "Can a 500-line class be fine?" Probing: size versus responsibility. Stalls: "No, it is too big." Moves up: yes, if every part changes for the same reason. Length is a hint, not the rule.
12
4. "What goes wrong if you overdo it?" Probing: balance. Stalls: "Nothing." Moves up: tiny classes that each do almost nothing, and one behaviour spread across fifteen files.
13
5. "How would you split this class?" Probing: method. Stalls: "By feature." Moves up: group by who asks for changes, confirm with the field check, and keep the outside interface the same while you move things.
14
  Two masters, one class
15
    class OrderReport {
        BigDecimal calculateTotal()    // finance decides the rules
        String formatHtml()            // design decides the layout
        void saveToDatabase()          // platform decides storage
    }
16
    Three actors. A tax change, a redesign and a schema migration all
    edit the same file, and each risks the other two.
17
    Split by actor:
        OrderCalculator  -> finance
        OrderRenderer    -> design
        OrderRepository  -> platform
18
  The cohesion check
19
    fields:  a, b, c, d
    method1 uses a, b
    method2 uses a, b
    method3 uses c, d
    method4 uses c, d
    -> two clusters sharing no state: two classes in one.
20
  Over-application, what it looks like
21
    OrderService -> OrderValidator -> OrderValidationRule ->
      OrderValidationRuleEvaluator -> ...
22
    Each class has one method that calls the next. Nothing is
    reusable, nothing is independently testable in a meaningful
    way, and reading one behaviour means opening the whole chain.
23
  A usable rule of thumb
    Ask "who asks for this to change?" If the answers differ,
    split. If they are the same, leave it -- even at 500 lines.