Single Responsibility Principle

Nitin Rohidas
6 min readApr 10, 2020

Single Responsibility Principle aka SRP is one of the most important but also the most misunderstood of the Software Engineering Principles. It says that a class or a module should have Single responsibility, but it doesn’t defines what does ‘responsibility’ really mean? How do I define the scope of my responsibility after all, responsibility could be as big as saving the planet or as small as printing out something on console. After digging further through the web over this topic for my own clarity, I believe that the original principle of SRP which as stated by Robert C. Martin was kind of incomplete.

“A class should have only one reason to change.”

Photo by Tadeusz Lakota on Unsplash

Bit Puzzled? The real question is how do I differentiate between ‘Reason’ Vs ‘Responsibility’. For example, I am a class (PackTiffinbox )responsible for packing Tiffins for my kid.Which reasons could impact this responsibility of mine?

probable reasons —

  1. Tiffin size increased
  2. Food chart got changed
  3. Packing material needs to be changed

Of all the reasons mentioned above, my responsibility still didn’t change as all the reasons mentioned above were impacting only one clearly defined responsibility of mine i.e — packing Tiffin. If the reason for changes are multiple but very closely related, it means you have defined the responsibility (packing Tiffin) well enough.

However, this is not always easy. One can define a broader responsibility and accommodate multiple reasons for change aligning to that single responsibility. This is where we could fail and we have in the past. Check any legacy code in your code base and you will find many classes with 1000+ lines of code and with ‘n’ number of functions defined in a single class ,often putting too many responsibilities in one class. To fix, this mess, SRP became popular and developers got a shiny bullet to fix their messy code base. However, even with the right intent, they started applying SRP foolishly which caused further issues.

With lack of understanding of what Uncle Bob really meant, enough damage was already done with respect to fragmentation of code by creating unnecessary classes. Some purist went ahead to say that a class should have only a single function. The other functions — misjudged as another ‘unit’ of responsibility were to be coded as a separate classes with its own set of methods. Sounds dumb, right? But this has actually led to poor design of the system causing maintainability issues. Change requests coming from the business which should ideally impact ‘limited’ components were now scattered in multiple classes and packages.

To illustrate this point further. Look at the example below

class Car
{
int weight;
void startEngine();
void stopEngine();
}

Can you break up this class more ? May be by defining a separate class for the engine ? Okay, lets do it.

class Engine
{
start();
stop();
}

class Car
{
Engine engine;
int weight;
}

Seems good ?. A change coming from the Engine company (if the Engine is outsourced( FIAT) or from in-house Engine department, will only impact the Engine class, it doesn’t not have any direct impact to Car class. The responsibility segregation has been achieved in true sense.

But, I am lured so much now to break this class even further. Can you break up further based on starting and stopping responsibility? Yes, Why not?

class EngineStop
{
stop();
}

class EngineStart
{
start();
}

class Car
{
EngineStart engineStart;
EngineStop engineStop;
int weight;
}

But the resultant OOP model is very fragile as starting and stopping engines are just operations performed on an Engine , it is not high level abstraction in itself. Too much abstraction violates this principle. This is where you should STOP.

To reduce this overall misunderstanding of the SRP — Robert Martin introduced further clarity by stating the following -

Gather together the things that change for the same reasons. Separate those things that change for different reasons.

It’s pretty simple but very important point. The code fragmentation caused by surfeited application of SRP could now be controlled. This quote actually talks about Coupling and Cohesion (C&C). We have known about Coupling & Cohesion terms since we started learning about computing systems in our universities, but inexperienced developers seldom give much thoughts on these factors while building new functionality. The aim should always be to build systems with high Cohesion and loose Coupling.C&C are basically architectural concerns and an experienced designer should factor it while designing systems and components. Having said that , one can also easily fall in the trap of breaking one principle while trying to apply another principle. For example — in the quest to do SRP rigidly, we miss to gather together things which should form a highly cohesive component.

Consider this bit of Java code:

public class Employee {
public Money calculatePay();
public void save();
public String reportHours();
}

The class is doing three different operations but all the operations are related to the Employee. However, the end user of these functions are different user groups. The Finance department would like to use the calculatePay() method. The MIS reporting team would use the reportHours() . A change coming from ‘HR department’ to change the payment calculation could break the MIS Reporting service data and vice versa . We wouldn’t want this to happen in the real world. Change for a particular user group should not impact other user group. Martin explains this aspect with a nice example.He emphasis that the true “reason for change” can be understood by asking following questions-

What is the program responsible for?

Or, perhaps a better question is: who is the program responsible to?

Better yet: who must the design of the program respond to?

If we can answer these question correctly, we would realize that our programs are responsible towards some stakeholders i.e. group of people or some key business person/customer.Only when these people initiate or request a change, our systems are impacted to fulfill their request. Our design should then respond to such changes in a least invasive manner.

And this gets to the crux of the Single Responsibility Principle. This principle is about people.

The above class should be broken down in to two separate classes as each class will be responsible to serve a different user group and any future enhancements and changes will be easy to handle with minimum side effects.

public class EmployeePay {
public Money calculatePay();//calculates pay based on his skill
public void save();

}
public class EmployeeReporting {
public String reportHours(); //returns report of his working hours
}

This was one of the crucial aspect I learned about SRP as explained by Martin.

Modern architectures like Micro-services also uses the same principle but at very high level in defining boundaries of a micro-service. Microservices needs to be very loosely coupled and any loose ends in defining the boundary correctly can give nightmares to the team maintaining those microservies. It is a challenging task to design microservices as it requires niche skills both from technical as well as from the business perspective. Modern practices like Domain Driven Design helps to build rich software models of your complex business domain and Bounded Context is a central pattern in Domain-Driven Design which aims to define the responsibility of the microservices with the contextual understanding of the business domain. In terms of deciding the responsibility of a microservice, the context is an important aspect which is often ignored. DDD ensures that the real world contextual understanding of your business model is preserved and well defined in your software models. Do read about it if you want to explore more.

Hope you got to learn about SRP in the most simplistic way possible. Please reach out to me for any questions or suggestions.

--

--

Nitin Rohidas

Software Architect by profession, Tech Enthusiast, Spiritual aspirant, loves to write & exploring new areas of interest — writing, psychology, philosophy.