TheDebugosaurus

This is an old article

My opinions on this have likely changed since this was first written

The single responsibility principle

SOLID seems to be the hardest word

I often like to ask job candidates which of the SOLID principles they’d say is the most important of the five. This tends to be a good starter for discussions and helps separate those with a working knowledge of SOLID from those who learned the Wikipedia definitions the night before. More often than not candidates will pick the Single Responsibility Principle, or SRP, before anything else. Similarly in a straw poll at a recent meetup upwards of 70% of developers choose the SRP as the key principle of SOLID design.

I believe this is probably due to the nature of the principle in that, of all the five, it has the most obvious and tangible impact to code composition and design. It’s quite easy to envision how SRP can be applied to code, and it’s not too much of a stretch to be able to reason about the immediate benefits of a codebase logically separated along clear responsibilities.

What is it?

A class should have one, and only one, reason to change Rob (Uncle Bob) Martin

This is the classic definition of the SRP as offered by Robert (Uncle Bob) Martin, and is the explanation I like best as I feel it gets to the heart of the principle. The SRP is all about ensuring that classes are defined with a clear and singular reason to exist. Classes that have multiple reasons to change tend to be juggling multiple responsibilities, which can lead to problems with maintenance and future extension.

Benefits

The immediate benefits of the SRP stem from it being able to split up larger codebases into smaller, more maintainable classes. By reducing the scope of classes it stands to reason that you should be able to reduce complexity which in turn should yield more readable, maintainable code.

With strongly segregated responsibilities you help reduce the impact of modifications to the system. By splitting up responsibilities you reduce the possibility that changes to one class will impact or change otherwise unrelated functionality.

Beyond that by sticking to the SRP you are in part enabling an Open / Closed Principle (OCP) compliant system. A class with only a single responsibility is less likely to require modifications than a class with a multitude of responsibilities. The fewer responsibilities a class has, the more closed it can be.

With reduced complexity also comes increased testability. By separating out responsibilities it should in theory be easier to test the smaller classes with greater confidence and coverage than if they were amalgamated.

It does not mean only do one thing

I’ve heard the case made, albeit over a beer or two, that if statements are a violation of the SRP. The argument goes that in order to code a functional if statement you need to do two things:

if(evaluate-condition)
{
    execute-statement
}

The above code is doing two things, evaluating some condition, and then subsequently executing a different statement. This does not necessarily make it an SRP violation, that determination comes only from examining the intent and scope of both statements.

Problems with SRP

While there are some immediately obvious benefits the SRP is not without its problems. As with any software principle strict or overzealous enforcement without thought can exacerbate problems and lead to worse code.

Can’t see the wood for the trees

Once of the most common complaints leveled at the SRP is that strict adherence to it results in an explosion of micro-classes - making it very hard to find anything in your system.

It also becomes harder to debug code as you’re not sure where behaviour is actually defined; or something that was previously reasonably coherent has now been spread thinly over a number of classes - to the point that it is difficult to fathom how these classes interact together.

Overcoming this requires that you get reevaluate just how you’re scoping responsibilities, ensuring that you’re applying the SRP at a sensible scale - which leads to me the next problem…

Defining responsibilities

Defining exactly what a responsibility is can be problematic and is often the subject of debates between developers who prefer debating to actual work. There are arguments for and against most possible points on the scale from “if you can separate it and still maintain clarity of purpose then split it” through to “defining a responsibility as a business concern”.

I’ll dig a bit deeper into defining responsibilities in the next post, however my advice will likely be that you should use your own judgment regarding how big or small to sensibly split a class.