Single Responsibility Principle (SRP)
The Single Responsibility Principle is the “S” in SOLID. It states that:
“A class should have one, and only one, reason to change.” — Robert C. Martin
What is a “Reason to Change”?
A “reason to change” is synonymous with a “responsibility”. If a class handles multiple disparate tasks, it has multiple responsibilities.
The Problem with Multiple Responsibilities
- Fragility: Changing one responsibility might accidentally break another.
- Low Cohesion: The class becomes bloated and hard to understand.
- Difficult Testing: You have to mock many unrelated things to test one function.
Example: The Multi-Purpose User Class
The Bad Way (Violating SRP)
This class handles user data, validation, and database saving.
class User:
def __init__(self, username, email):
self.username = username
self.email = email
def validate_email(self):
return "@" in self.email
def save_to_db(self):
print(f"Saving {self.username} to database...")
If the database schema changes, we change the User class. If the validation logic changes, we change the User class. This class has two reasons to change.
The Good Way (Applying SRP)
We split the responsibilities into separate classes.
class User:
def __init__(self, username, email):
self.username = username
self.email = email
class UserValidator:
@staticmethod
def validate(user):
return "@" in user.email
class UserRepository:
def save(self, user):
print(f"Persisting {user.username} to DB...")
SRP in C++
Consider a class that manages a Report and also prints it.
// Violation
class Report {
string content;
public:
void generate() { /* ... */ }
void print() { cout << content << endl; } // Printing logic mixed with data logic
};
// Fixed
class Report {
string content;
public:
string getContent() { return content; }
};
class ReportPrinter {
public:
void print(Report& r) { cout << r.getContent() << endl; }
};
How to Identify SRP Violations
- Size: Very large classes often have too many responsibilities.
- Imports: A high number of imports from unrelated modules.
- Documentation: If you find yourself using “and” to describe what a class does (e.g., “This class parses data AND sends emails”).
- Frequent Changes: If different teams are constantly touching the same file for different reasons.
Benefits of SRP
- Maintainability: Smaller classes are easier to read and modify.
- Reusability:
UserValidatorcan be reused in different parts of the app without bringing in the database logic. - Testability: Unit tests for
UserValidatordon’t need to know about databases.