The Single Responsibility Principle (SRP) states that a class should have only one reason to change. In other words, a class should have a single responsibility or a single job. This principle promotes the idea of separating concerns and avoiding classes that try to do too much, which can lead to code that is difficult to understand, maintain, and test.
Let's consider a practical use case to understand the SRP:
Use Case: User Registration System
Example: Suppose we are developing a user registration system for a website. The system allows users to create an account, validate user data, and persist user information to a database.
Without applying the SRP:In a naive implementation, we might have a single UserRegistration class that handles all aspects of user registration, including input validation, data persistence, and email notifications. This class would have methods like registerUser(), validateInput(), persistUserData(), and sendConfirmationEmail(). However, this violates the SRP because the class is responsible for multiple tasks.
With Applying the SRP:To apply the SRP, we can refactor the UserRegistration class into separate classes, each with a single responsibility. Here's a Java example demonstrating the Single Responsibility Principle (SRP):
UserValidator - User class: This class is responsible for validating user input data, such as email, password, and username. It would have a method like validateUserInput() that takes user input as parameters and returns whether the input is valid or not. This class focuses solely on input validation and does not deal with persistence or notifications.
* Represents a user entity with properties like username, email, and password.
* Includes a method validateInput() that performs validation logic for username, email, and password. This method ensures that the user input is valid.
* The User class focuses solely on user-related properties and validation.
public class User {
private String username;
private String email;
private String password;
// constructor, getters, setters
public boolean validateInput() {
// validation logic for username, email, and password
// return true if input is valid, false otherwise
}
}
UserRepository class: This class handles the persistence of user data to the database. It would have methods like saveUser(), getUserById(), or getUserByEmail(). The UserRepository class is responsible only for interacting with the database and does not handle input validation or notifications.
* Handles the persistence of user data to the database.
* Includes methods such as saveUser(), getUserById(), and getUserByEmail().
* This class is responsible for interacting with the database and manages user data storage and retrieval.
public class UserRepository {
public void saveUser(User user) {
// logic to persist user data to the database
}
public User getUserById(int userId) {
// logic to retrieve a user from the database by ID
// return the User object if found, or null if not found
}
public User getUserByEmail(String email) {
// logic to retrieve a user from the database by email
// return the User object if found, or null if not found
}
}
EmailService class: This class handles sending email notifications to users. It would have a method like sendEmail(), which takes user details as parameters and sends an email for account confirmation or other notifications. The EmailService class is dedicated to email functionality and does not perform input validation or database operations.
* Handles sending emails to users.
* Includes a method sendEmail() that takes a User object and a message as parameters to send an email to the user.
* This class is dedicated to email functionality and is responsible only for sending email notifications.
public class EmailService {
public void sendEmail(User user, String message) {
// logic to send an email to the user
}
}
By splitting responsibilities into separate classes, each class has a clear and single purpose. The User class handles user-related data and validation, the UserRepository class manages data persistence, and the EmailService class focuses on sending emails. each class adheres to the SRP, making the codebase more modular and maintainable. If, for example, a change is required in the email notification process, it can be done in the EmailService class without affecting the UserValidator or UserRepository classes. Likewise, modifications to input validation or data persistence can be made in their respective classes without impacting other components. This separation of concerns promotes code re-usability and simplifies testing and debugging.