Aaron Li

Solid: Single Responsibility Principle (SRP)

Table of Content

Single-responsibility Principle (SRP) states

A class should have one and only one reason to change, meaning that a class should have only one job.

It means that it should have only one responsibility. A responsibility, in this context, refers to a reason for a class to exist or a specific task that it performs.

Example with Violation (Invalid)

Class

class User {
    private let username;
    private let email;
    ...

    save() {
        // Code to save the user to the database.
    }

    sendEmail(message) {
        // Code to send an email to the user.
    }
}

In this example, the User class has two responsibilities:

  1. Handling database operations (saving a user).
  2. Sending email to the user.

This violates SRP because if there's a change in the way users are saved or if the email sending logic needs to be modified, you have to modify the same class, which deals with two unrelated tasks.

function


const calculateTotalAndNotify = (cartItems, customerEmail) => {
    let totalPrice = 0
    for (let i = 0 ; i < cartItems.length ; i += 1) {
      totalPrice += cartItems[i].price
    }

    sendEmail(customerEmail, `Thank you for your purchase! Total: ${totalPrice}`)
}

In this example, the calculateTotalAndNotify function has two responsibilities:

  1. Calculating the total price of items in the shopping cart.
  2. Sending an email notification to the customer.

This violates SRP because a single function should not be responsible for both calculation and email notification. If any change is required in either task, you'll have to modify this function, which can lead to unintended consequences.

Example following SRP (Valid)

Class

class User {
  ...
}

class UserRepository {
    save(user) {
        // Code to save the user to the database.
    }
}

class EmailService {
    sendEmail(user, String message) {
        // Code to send an email to the user.
    }
}

By doing this, we have adhered to SRP, and now the User class is only responsible for representing user data. The database operation and email sending tasks are now handled by separate classes that have their own single responsibility.

function

const calculateTotalPrice = (cartItems) => {
  let totalPrice = 0
  for (let i = 0 ; i < cartItems.length ; i += 1) {
    totalPrice += cartItems[i].price
  }
  return totalPrice
}

const notifiyCustomerPrice = (totalPrice, customerEmail) => {
  sendEmail(customerEmail, `Thank you for your purchase! Total: ${totalPrice}`)
}

Benefits of SRP

  1. Readability and Comprehension: Functions with a single responsibility are easier to read and understand because they are more focused and have clear purposes.

  2. Maintainability: Splitting responsibilities into separate functions makes it easier to maintain and modify the code. Changes to one aspect won't affect other unrelated parts.

  3. Reusability: Functions that perform a single task are more reusable in different contexts.

  4. Testability: With focused responsibilities, you can write targeted and specific test cases for each function, leading to more effective testing.