Single Responsibility Principle Explained

Meet Ben and Tony, two typical employees of an e-commerce company called WooMinus. Ben is a crazy accountant, who can’t stop thinking about cents and euros, while Tony is a product owner who finds some peculiar joy in reports and wiki pages. Our two characters will be assisting us in our way deep into SOLID principles. We’ll start our journey with The Single Responsibility Principle.

Where’s my money?!

Ben is going crazy today. Something wrong happened to the tax calculation and company started paying the goverment way more money than necessary. He comes to you, slaps you in the face and threats that if you don’t find the cause of the problem in the next 10 minutes, he’ll report you to the CFO and make sure all sites with sweet kitties are blocked by company’s proxy forever. With that much at stake, you turn on your superpowers and find the issue almost immediately:

It turned out that the developer responsible for accelerating report generation for Tony modified the calculateVat  method to use naive rounding instead of the sophisitacated tax calculation algorithm. In the burden of other stuff present in OrderService  class, he didn’t notice that the method is also used by the calculateTaxes  method. Of course, we could argue that there should be proper tests and peer review and {put your preferred practice here} in place, but there is one obvious issue with this class – it does too much!

The Single Responsibility Principle

Every change done in class’ logic can impact rest of the logic contained in that class. In our example, a change done to VAT calculation for the purpose of reporting impacted both report generation and tax calculation process. Likewise, a change for the purpose of improving tax calculation could have affected the report generation. We clearly see that changes to two unrelated processes impact each other, because they share a common class. That’s where The Single Responsibility Principle comes in:

A class should have one reason to change.

By the virtue of this principle the two unrelated processes, as two unrelated reasons to change, should be put in separate classes.

Now, the two classes won’t probably share the VAT calculation, but if they do…

Who owns the tax calculation?

That’s the other side of The Single Responsibility Principle. To make things clearer, I’ll modify the code a little bit:

Now, despite the fact that report generation and tax calculation are in separate classes, they still share some code. If Tony asks for some change to the report, Ben’s cents and euros might be in danger. Therefore, it’s crucial to remember:

The Single Responsibility Principle is [also] about people.

If there’s two parties / people with divergent requirements interested in class’ functionality, there are two reasons to change that class and therefore SRP is not satisfied!

Now, to make both Ben and Tony happy, they deserve their separate classes for their separate requirements:

The Reality Check

Once you notice the possible negative consequences of mixed responsibilities, it might be tempting to go hunting and split your classes into smallest possible pieces, so that no extra reason to change gets tangled anywhere. This other extreme is also bad, so before you do that, I’d like to add a bit of sanity ingredient to our mix. At the root of SRP there are cohesion and coupling, first of which is about keeping related stuff together. Therefore, same as you shouldn’t bash everything into a single class, you also shouldn’t split a cohesive class apart.

A really skewed example of applying SRP was presented in DZone’s Shades of the Single Responsibility Principle. The author suggests to split a CRUD service like this:

At first, it might seem reasonable, because each of these operations is (probably) independent, we can’t judge about interested people with this little information, so maybe it makes sense to make four services instead of one? (Hey, maybe let’s throw Ben and Tony in the example and make it eight?!)

First thing that I’d question about this example is whether PersonService is actually a service or a repository. The methods it contains suggest persistence operations, not real business cases. Maybe the service layer is completely redundant here? Maybe a controller hitting a repository directly is the right approach here?

Yet in some projects, such CRUD orientation is reality and each of the operations actually contains additional business logic. The questions we need to ask at this point are: how much? and how important is the independence of these operations for the stakeholders? What I mean by this is that if this “additional business logic” is a few, trivial lines of code, maybe there’s no risk involved and there’s simplicity benefit to keeping those in one place.

I think that this last example nicely leads us to my final advice in this article: Use The Single Responsibility Principle wisely, as a problem prevention or problem solving tool, not as an excuse to stop thinking.

About the Author Grzegorz Ziemoński

King of Tidy Java, nerd that thinks about producing perfect software all the time and proud owner of 2 cats.

follow me on: