“A class should have one and only one reason to change”
The Single Responsibility Principle (SRP) is one of the five SOLID principles of object-oriented programming. Despite its straightforward definition, the SRP can be challenging to apply in real-world settings.
Formal Definition
Let us look at a more formal definition for SRP -
The Single Responsibility Principle (SRP) states that a module should be responsible to one, and only one, actor. An actor in this context is any individual, group, or other system that interacts with the system in a way and may call for modifications to the module.
When a module/class is responsible to a single actor, any change to that module will only occur when requirements from that actor changes. This is why the above formal definition is also loosely stated as -
A class should have one and only one reason to change.
Responsibilities
In the context of SRP, responsibility denotes a reason to change. If a class can be change because of more than one reasons, that class has more than one responsibility. Such a class does not follow SRP.
Checking if a class has more than one responsibilities is a tricky task! Consider the following example -
class User {
string name;
int age;
bool isAdmin;
public:
virtual void registerUser() = 0;
virtual void sendWelcomeEmail() = 0;
};
At first glance, this class appears to be following the Single Responsibility Principle. This is because the User class is responsible only to a single actor called User. But on a second thought this turns out to be incorrect.
Even though User is a single entity in real world, it is not a single actor in software design. A User can act in three ways and thus it leads to three different actors! Following are the responsibilities associated with each of these three actors -
- Storing User related information such as name, age, and admin status.
- Registering Users to Database.
- Sending welcome emails to User.
These three responsibilities are not closely related, and changes to one responsibility could affect the other responsibilities. For example, if we only wanted to utilise the first name when sending welcome emails, we would need to adjust the way the User information are stored.
Once we identify a class that handles more than one responsibility, we know that it can be broken down into smaller classes that each handle one responsibility. Here is how we can fix our User class.
class User {
string name;
int age;
bool isAdmin;
public:
virtual void getFirstName() = 0;
virtual void getLastName() = 0;
virtual void getName() = 0;
virtual void getAge() = 0;
virtual void isUserAdmin() = 0;
};
class UserRepository {
public:
virtual User& registerUser(const User& user) = 0;
virtual User& updateUserName(const User& user) = 0;
};
class UserService {
public:
virtual void sendUserWelcomeEmail(const User& user) = 0;
virtual void sendPromotionEmailToAllUsers() = 0;
};
Now each class has one and only one responsibility!
Cohesion Vs Coupling
Before closing on this topic, I wanted to discuss the difference between Cohesion and Coupling. Most of the articles out there seems to not cover this aspect of SRP.
We define Coupling as the degree of interdependence between software modules. We generally want to avoid any unnecessary coupling between software modules. This leads to better and maintainable softwares.
We define Cohesion as the degree to which the elements inside a module belong together. It means that code that changes together stays together.
In our softwares, we want to achieve higher levels of decoupling while maintaining related components together in a single module. Thus software design is nothing but a complex problem where we try to achieve perfect balance between cohesiveness and decoupling.
We want to create independent modules for each new responsibility but not multiple modules for a single responsibility. This is exactly what SRP principle states!
As mentioned in the beginning of this article, understanding SRP is easy but to apply it is equally difficult. But following SRP keeps the code separated, organised, and maintained.
The difficult in applying SRP comes from its inherent subjective nature. A class that might look good to one person can be violating SRP from other’s perspective.
Mastering this principle can really assist any developer in their journey to better softwares!
Top comments (0)