DEV Community

Cover image for SOLID: S - Single Responsibility Principle (SRP)
Paulo Messias
Paulo Messias

Posted on

SOLID: S - Single Responsibility Principle (SRP)

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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!');
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
}
Enter fullscreen mode Exit fullscreen mode

Bad Practice Example (Functions):

function addTaskAndNotify(task: Task): void {
  // Logic to add task
  // Logic to notify that the task is due
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
skanderlassoue1 profile image
Skander Lassoued

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 ?

Collapse
 
pht_thanhlm_5c50233c2c profile image
Pickle Lam

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

Image description