The Single Responsibility Principle
I reused the canonical example from Robert C. Martin’s excellent book ‘Clean Architecture‘: We have an
Employee class in a payroll application responsible to three separate ‘actors‘—the IT, Accounting and Operations departments. And herein lay the problem—any changes to the
Employee class made by one of the departments might be at odds with the needs of one of the other departments. A modification of a shared helper method by the programmers in Accounting consequently broke expected behaviour in the Operations department.
SRP violations can express themselves as subtle, damaging bugs.
What are we to do? How do we fix this?
Let’s refresh our minds of what the (restated) SRP was about:
Each module should only be responsible to one actor.
How do we make class
Employee responsible to only one actor? We may need to break it up into smaller classes, each solely accountable to a single department’s needs.
How should we go about this?
Well, the public methods of
Save(). Each is responsible to a different actor—the Accounting, Operations and IT departments, respectively.
It makes sense to break up
Employee along these identified responsibilities, the public methods. But what should we name the new classes?
The names of modules should reflect the work that they do. In our case, it would make sense to have, say, a
HoursReporter and an
EmployeeSaver. Those names are pretty good, I think. No?
Aside – No Noise Words
Please, please give your classes descriptive names that leave out meaningless noise words. Consider
EmployeeSavingService. Do these names convey more information than
EmployeeSaver? No, not really. Their wishy-washy naming reduces clarity and obscures meaning. Indeed, these unclear names may, over time, attract code that does not strictly belong. The often-used ‘
Manager‘ and ‘
Service‘ noise words detract rather than clarify a classes purpose and are best left out.
The classes after breaking up of class
A couple of salient points:
- Notice that
HoursReportereach have their own, independent version of
GetRegularHours(). We got into trouble because a change to
GetRegularHours()by the Accounting department broke expected behaviour in Operations. Both
HoursReporternow have separate, independently changeable copies of
GetRegularHours(). The Accounting and Operations departments calculate regular employee hours worked differently. We are forgoing DRY (Don’t Repeat Yourself) because it is appropriate, even required, for us to do so.
- Our new collection of classes must not reference and call one another. If they do, then we will have re-established the original SRP violation.