Introduction to SRP:
The Single Responsibility Principle (SRP) is one of the five SOLID principles, a set of guidelines for writing cleaner and more sustainable code. SRP states that a class should have only one reason to change, meaning it should have only one responsibility or function. Following this principle makes the code easier to understand, maintain, and test.
Objectives of SRP:
- Simplified Maintenance: With classes having only one responsibility, identifying and fixing bugs becomes easier.
- Clear Responsibility: Each class has a clear purpose, making the code easier to understand.
- Improved Testability: Classes with single responsibilities are easier to isolate and test.
- Ease of Change: Changes in a specific responsibility do not affect other parts of the system.
Bad Practice Example (Classes):
Here we have a UserService
class that does more than one thing: manages users and sends notifications.
class UserService {
createUser(user: User): void {
// Logic to create user
}
deleteUser(userId: string): void {
// Logic to delete user
}
notifyUser(userId: string, message: string): void {
// Logic to notify user
}
}
In this approach, the UserService
class has multiple responsibilities: managing users and sending notifications. This violates SRP.
Good Practice Example (Classes):
To apply SRP, we can separate the responsibilities into distinct classes.
class UserService {
createUser(user: User): void {
// Logic to create user
}
deleteUser(userId: string): void {
// Logic to delete user
}
}
class NotificationService {
notifyUser(userId: string, message: string): void {
// Logic to notify user
}
}
Now, UserService
handles only user creation and deletion, while NotificationService
handles notifications. Each class has a single responsibility, following SRP.
Bad Practice Example (Functions):
Here we have a function that does more than one thing: creates a user and sends a notification.
function createUserAndNotify(user: User, message: string): void {
// Logic to create user
// Logic to send notification
}
In this approach, the createUserAndNotify
function has multiple responsibilities: creating a user and sending a notification. This violates SRP.
Good Practice Example (Functions):
To apply SRP, we can separate the responsibilities into distinct functions.
function createUser(user: User): void {
// Logic to create user
}
function notifyUser(userId: string, message: string): void {
// Logic to notify user
}
// Using the separated functions
createUser(newUser);
notifyUser(newUser.id, 'Welcome!');
Now, the createUser
function handles only user creation, while notifyUser
handles notifications. Each function has a single responsibility, following SRP.
Application in React Native with TypeScript:
Imagine we are developing a task management app. We can apply SRP by separating task management logic and notification logic into different classes.
Bad Practice Example (Classes):
class TaskService {
addTask(task: Task): void {
// Logic to add task
}
removeTask(taskId: string): void {
// Logic to remove task
}
notifyTaskDue(taskId: string): void {
// Logic to notify that the task is due
}
}
Good Practice Example (Classes):
class TaskService {
addTask(task: Task): void {
// Logic to add task
}
removeTask(taskId: string): void {
// Logic to remove task
}
}
class TaskNotificationService {
notifyTaskDue(taskId: string): void {
// Logic to notify that the task is due
}
}
Bad Practice Example (Functions):
function addTaskAndNotify(task: Task): void {
// Logic to add task
// Logic to notify that the task is due
}
Good Practice Example (Functions):
function addTask(task: Task): void {
// Logic to add task
}
function notifyTaskDue(taskId: string): void {
// Logic to notify that the task is due
}
// Using the separated functions
addTask(newTask);
notifyTaskDue(newTask.id);
By dividing responsibilities, we make the application easier to maintain and expand.
Conclusion:
Following the Single Responsibility Principle helps keep the code clean, organized, and easier to maintain. Applying SRP in React Native development with TypeScript results in more modular and testable code. Always remember to keep your classes and functions focused on a single responsibility to reap all the benefits of this principle.
Top comments (2)
Very important article but I have a question:
asuming we have
`addTask(task: Task): void {
// Logic to add task
}
notifyTaskDue(taskId: string): void {
// Logic to notify that the task is due
}`
then i need to call notifyTaskDue() in addTask() to pass specific params as shown below:
` addTask(task: Task): void {
// Logic to add task
this.notifyTaskDue(task.taskId)
}
notifyTaskDue(taskId: string): void {
// Logic to notify that the task is due
}`
Does this violate SRP, the same for classes if i want to inject A in B does it violate SRP ?
based on your snippet code, I will answer it as I understand for applying this pattern many times.
You did violate the SRP because your "addTask" just did add a task, then u put the "notifyTaskDue" into the method "addTask" that can make the addTask do two purpose.
You should create an own business logic for handling your own logic and call both methods like that.
I will assume like that =>
BusinessLogicService => you inject both TaskService and NotificationService => You do something with your code and then call