Single Responsibility Principle
5 1 vote
Article Rating

Do you really know, what is Single Responsibility Principle?

The Single Responsibility Principle is tricky to follow, but why? Let’s dig into it.

Introduction

I believe that every developer should be familiar with the SOLID principles. The SOLID principles are comprised of five principles that all aim for the same purpose: writing understandable, readable, maintainable, and testable code, especially in the OO style that many developers can collaboratively work on.

SOLID is a helpful acronym you can use to remember five essential principles:

When and Where Should I Use the SOLID Principles?

Before diving into the first principle, let’s know what is PDD.

PDD is Pain-Driven Development. It means you should write your code as simple as possible to solve a specific problem, not worrying about SOLID. Because applying the SOLID upfront is premature optimization.

Instead, look for places in your code where the app is painful to work with as your app grows. This pain may be due to coupling, many duplications, or difficulty in testing.

When you get there, see if any SOLID principles you can apply to improve your design and mitigate this pain.

In this article, let’s begin with the SOLID first principle, the Single Responsibility Principle (SRP).

What Is the Single Responsibility Principle?

Robert C. Martin (Uncle Bob) defines the Single Responsibility Principle as:

“Each software module should have one and only one reason to change.”

Uncle Bob

You can think this definition is so easy to implement. Or in contrast, you can think of some questions like what is the module exactly? What is the reason to change? And what does it relate to the responsibilities?

To be honest, this definition was very tricky for me. I was always thinking of these questions. So I am writing this article to share my approach to following this principle.

OK, first of all, you can think of a module as a function, class, component, or even microservice.

To better understand this principle, we must keep another two principles in mind, encapsulation, and delegation. But why?

I think we agree that the better we’re able to separate the what and the how a class does, the better our software design is. This separation could be done using encapsulation and delegation.

A class should encapsulate doing a specific task in a specific way. And when this class is a single-purpose, it does this purpose perfectly, and we can use it easily.

Or a class can delegate a specific task to another instance of a class that encapsulates doing this task in an abstracted way.

On the other hand, when a class is multi-purpose, it often ends up coupling things that shouldn’t be related together and making it harder to use.

Single Responsibility Principle - Encapsulation and delegation
Encapsulation and delegation

As you can see, the Auth class has a method login that is responsible for its business logic and sending an SMS to the user after logging in. The Auth class delegates sending the SMS task to another class SmsProvider which encapsulates its implementation in a way that the Auth class doesn’t know how the SmsProvider does its implementation.

What Is the Responsibility? And What About the Reason To Change?

Responsibility is the answer to the question of how something is done.

Responsibility at the code level might be data persistence, logging, validation, third-party integration, or the business logic itself. The distributed system level might be caching, message queuing, proxy, or load balancer.

SRP suggests that every module has one reason to change and has one responsibility. Each of these responsibilities may be changed in the future. For example, data persistence may be changed from files to databases or from one database to another. Validation criteria may be changed. The logging tool can be changed. Business logic is usually changed. The caching type may be changed. Or the message queuing tool may be changed.

Yes, defining the responsibility and reason to change is tricky. As a result, you have to be aware of any potential request for change that might violate the SRP in your code. These requests are the source of the reason to change. They might be requests from your managers or even improvements from your perspective.

The more you know about these requests, the more you can separate your decisions and apply the SRP perfectly.

To What Extent Should We Apply SRP?

Unfortunately, following the SRP sounds easier than it is.

Some developers take the SRP to the extreme by creating a class with just one method. And when they write actual code, they have to inject many classes, making the code more complex and unreadable.

You shouldn’t oversimplify your code. You have to use the SRP in common sense. There is no benefit if you create every class with only one method. In that case, why do classes exist, and why not go back to procedural programming?

You must define the balance point between your code’s over-simplicity and over-complexity. That point can be defined by:

  • Ask yourself, “What is the responsibility of this module?”. If your answer has the word “and,” you likely violate the SRP.
  • Ask yourself, “What are the potential reasons to change this module?”. If you have many reasons that don’t relate to each other, you likely violate the SRP, and your module is low cohesive and tightly coupled.
To What Extent Should We Apply the Single Responsibility Principle?
Photo by Dan Dennis from Unsplash

Relation Between SRP and Coupling, Cohesion, and Separation of Concerns

The SRP is closely related to the concept of coupling. When a class performs many details that aren’t related, these details are tightly coupled. And the more details a class has the more reasons for a change.

A loosely coupled class is responsible for some higher-level concerns and delegates to other classes responsible for the details of how to perform the lower-level operations for these concerns. This introduces us to another principle, Separation of Concerns.

Separation of Concerns suggests that a program should be separated into sections. Every section has to deal with one concern and should not know how another section does a specific task.

A key benefit of following the Separation of Concerns is that the higher-level code doesn’t have to deal with lower-level code and doesn’t know how the lower-level details are implemented.

Another concept that is closely related to the SRP is Cohesion. Cohesion refers to how strong the relationship between module elements is. The more a module has responsibilities, the lower cohesion between its elements.

Take a look at this example to understand these principles better, Class has three fields and two methods. method1 uses only field2 and doesn’t use the other fields. method2 uses field1 and field3 doesn’t use field2.

Single Responsibility Principle - Tight Coupling and Low Cohesion
Tight Coupling and Low Cohesion

This diagram depicts that Class might be tightly coupled, low cohesive, and doesn’t separate its concerns. So let’s try to refactor it with these principles in our mind:

Single Responsibility Principle - Loose Coupling and High Cohesion
Loose Coupling and High Cohesion

Now, we can say that Class1 and Class2 are highly cohesive, loosely coupled, and concerns are separated perfectly.

Why Should We Apply the Single Responsibility Principle?

Now, we know where and when we should use the SRP, but why should we bother ourselves by applying it?

Many benefits come from applying the SRP:

  1. We know that requirements change over time. Each change affects the responsibility of at least one class. The more responsibilities your class has the more reasons for changes you have.
  2. The single-purpose module is much easier to read, explain, and understand. You may remember how frustrated you feel when you have to refactor a big class.
  3. For sure, separated modules are more flexible and configurable than many responsible modules. If you have a big class and want to add a new feature or make a feature configurable, yes, you can do that in the class itself but only by increasing the class size and complexity.
  4. Single responsible modules are likely more reusable than many responsible modules. You may note that the methods with only one purpose most likely have no side effects and don’t depend on the class state. Yes, you are right as you thought. It is functional programming. The SRP nudges us toward the functional programming style.
  5. It’s away easier to test and maintain single-purpose modules.
  6. Having classes and methods with only one purpose helps to investigate performance issues easily. And more remarkable at the level of the distributed systems, services that only have one purpose are easier to monitor the load and resources bottlenecks and, as a result, scale up or down independently.
  7. If your class depends on one class or more, any change in this class would affect its dependencies. You might need to update these dependencies or recompile them even though they are not directly affected by your change.
I’m Convinced, I Will Use the Single Responsibility Principle Every Time, and Everywhere
Photo by Nadine Shaabana from Unsplash

Keep in mind these points before applying SRP

Having said that, you might think: I’m convinced, I will use the SRP every time, and everywhere. Before going that far, keep in mind these points:

  1. At the level of distributed systems, the more services you build, the low reliability you get. Yes, there are many ways to overcome this point, but you must remember that everything has its cost of time, effort, and money.
  2. As we know, separating concerns increases the code size and the effort and time you need to write this code. However, in the long term, it decreases the effort and time.
  3. Indeed, separating concerns in our code hits the overall performance. However, this point could be neglected at the level of code, but at the level of distributed systems, it hits a lot. Many services directly affect the system performance through higher latency and networking issues.

Let’s Apply SRP to an Example

Let’s introduce a simple example that introduces a class with many reasons to change and then try applying the SRP to it.

First of all, let’s make our investigation and ask ourselves:

  • What is the responsibility of this class? You might say it only registers a user. Another one might go deeper and say it is responsible for logging, validation, and persistence. OK, if you’re confused about identifying, jump into the next test.
  • What are the potential reasons to change this class? I think we agree that we may change the logging mechanism, the validation criteria, or the persistence approach, right? So we have many reasons to change this class. So it might be an alert to refactor your class and apply the SRP.

As a result. Let’s refactor this example with SRP in mind.

As you can see, there is more code in this version than in the previous version. That’s right, but the refactored version is easier to test, maintain, update, and readable. Now, the Auth class, which is the higher-level code, doesn’t know how the lower-level code is implemented. It only delegates other classes to do specific tasks encapsulating their implementations.

Conclusion

At the end of the day, the Single Responsibility Principle is essential. But be careful when using it. Use it only to eliminate pain by improving your design after writing some working code. Don’t oversimplify your code.

SRP helps you to achieve high cohesion, loose coupling, and separation of concerns.

Finally, keep your modules as small and simple as possible. Give them one responsibility and one reason to change. This eases your testability, maintainability, and readability.

Resources

Think about it

If you liked this article please rate and share it to spread the word, really, that encourages me a lot to create more content like this.

If you found my content helpful, it is a good idea to subscribe to my newsletter. Make sure that I respect your inbox so I'll never spam you, and you can unsubscribe at any time!

If you found this article useful, check out these articles as well:

Thanks a lot for staying with me up till this point. I hope you enjoy reading this article.

5 1 vote
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
Scroll to Top