2. SOLID - Single Responsibility Principle
A class or module should have only one reason to change
This is a continuation of the SOLID series Part 1.
The single responsibility principle is a software design principle that states that a class or module should have only one reason to change. Here is an example of code that violates the single responsibility principle
class UserService {
private userRepository: UserRepository;
constructor(userRepository: UserRepository) {
this.userRepository = userRepository;
}
public register(username: string, password: string): void {
// validate input
// create new user
// save user to database
}
public login(username: string, password: string): User {
// get user from database
// check password
// return user if password is correct
}
public sendEmail(user: User, subject: string, body: string): void {
// send email to user
}
}
In this example, the UserService class is responsible for registering users, logging them in, and sending them emails. However, these are three separate responsibilities, and the UserService class should only be responsible for registering and logging in users.
To fix this code, we can create separate classes for each responsibility:
class UserService {
private userRepository: UserRepository;
constructor(userRepository: UserRepository) {
this.userRepository = userRepository;
}
public register(username: string, password: string): void {
// validate input
// create new user
// save user to database
}
public login(username: string, password: string): User {
// get user from database
// check password
// return user if password is correct
}
}
class EmailService {
public send(user: User, subject: string, body: string): void {
// send email to user
}
}
Now, the UserService class is only responsible for registering and logging in users, and the EmailService class is responsible for sending emails. This adheres to the single responsibility principle and makes the code easier to maintain and understand.
Trade-offs
The UserService class is still responsible for both registering and logging in users, but this is a valid use of the single responsibility principle because these tasks are closely related. The key to the principle is that a class or module should have only one reason to change. In this case, the UserService class would only need to change if the way users are registered or logged in changes, which is a single responsibility. While you could create separate classes for these tasks, it may not be worth the added complexity in this case.
The single responsibility principle is a software design principle that states that a class or module should have only one reason to change. This is a subjective concept that depends on the specific use case and the design decisions made by the developer. There are a few effective ways to understand where to draw the line between adding more code and keeping it simple while still following the single responsibility principle.
Keep the single responsibility principle in mind when designing your classes or modules. As you write your code, ask yourself whether each class or module has only one reason to change. If it does not, consider refactoring your code to adhere to the principle.
Use common sense to divide your code into classes or modules. The single responsibility principle is a guideline, not a hard and fast rule, and it is up to you to decide how best to apply it in your specific use case.
Use test-driven development (TDD) to help design your classes or modules following the single responsibility principle. With TDD, you write tests for your code before writing it. This can help you identify responsibilities that should be separated into different classes or modules, and it can also help you avoid over-engineering your code.
Keep your code modular and flexible. By dividing your code into small, focused classes or modules, you can make it easier to change and maintain. This can help you follow the single responsibility principle without making your code overly complex.
As you work on your project, you may find that some classes or modules have more than one responsibility. In this case, you can refactor your code regularly to adhere to the single responsibility principle.