Single Responsibility Principle
In this chapter, we will explore the SOLID principles, a set of five design principles that help developers create maintainable and scalable software. These principles were introduced by Robert C. Martin and are widely regarded as best practices in object-oriented programming.
For starters, SOLID is an acronym that stands for:
- Single Responsibility Principle (SRP)
- Open/Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
The Single Responsibility Principle
Section titled “The Single Responsibility Principle”The Single Responsibility Principle (SRP) states that a software component (class, module, function, etc.) must have only one responsibility or one reason to change. This means that each component should be responsible for a single functionality or behavior.
Understanding the Principle
Section titled “Understanding the Principle”If you find yourself modifying a class for different reasons, it’s a sign that the abstraction is incorrect and the class has too many responsibilities. This indicates that you need to create more objects to address the extra responsibilities.
Objects that do one thing, and just one thing, are:
- Easier to maintain - Changes are isolated
- Easier to understand - Clear purpose
- Easier to test - Focused test cases
- Less prone to bugs - Fewer side effects
We want to avoid “God Objects” that know too much or do too much.
Example 1: User Management System
Section titled “Example 1: User Management System”Consider a user management system where a single class handles user data, validation, persistence, and email notifications. This violates SRP because the class has multiple reasons to change.
Violating SRP (Bad Approach)
Section titled “Violating SRP (Bad Approach)”Following SRP (Good Approach)
Section titled “Following SRP (Good Approach)”classDiagram
class User {
-username: str
-email: str
-password: str
}
class UserValidator {
+validate(user: User) bool
}
class UserRepository {
+save(user: User)
}
class EmailService {
+send_welcome_email(email: str)
}
class PasswordHasher {
+hash(password: str) str
}
UserValidator ..> User : validates
UserRepository ..> User : saves
EmailService ..> User : uses email
PasswordHasher ..> User : hashes password
Example 2: API Request Handler
Section titled “Example 2: API Request Handler”Another common violation occurs when a single class handles HTTP requests, business logic, data formatting, and database operations.
Violating SRP (Bad Approach)
Section titled “Violating SRP (Bad Approach)”Following SRP (Good Approach)
Section titled “Following SRP (Good Approach)”classDiagram
class APIHandler {
-user_service: UserService
-formatter: ResponseFormatter
+handle_request(request_data: dict)
}
class UserService {
-repository: UserRepository
+get_user(user_id: int)
}
class UserRepository {
+find_by_id(user_id: int)
}
class ResponseFormatter {
+format_success(data: dict)
+format_error(error: str)
}
APIHandler --> UserService
APIHandler --> ResponseFormatter
UserService --> UserRepository
Example 3: Report Generation System
Section titled “Example 3: Report Generation System”A report system that processes data, generates reports, handles file operations, and sends email notifications violates SRP.
Violating SRP (Bad Approach)
Section titled “Violating SRP (Bad Approach)”Following SRP (Good Approach)
Section titled “Following SRP (Good Approach)”Benefits of Following SRP
Section titled “Benefits of Following SRP”Identifying Violations
Section titled “Identifying Violations”Key Takeaways
Section titled “Key Takeaways”Remember: SRP is about cohesion - keeping related things together and unrelated things apart.