DEV Community

loading...
Cover image for Single Responsibility Principle

Single Responsibility Principle

Josué Rodríguez (He/Him)
A Honduran systems engineer trying to improve himself everyday.
・3 min read

The Single Responsibility Principle states that a class should have one and only one reason to change. It also states that each class should only have one job and do it well.

A class should have one and only one reason to change.

The reason behind this principle is to separate responsibilities or behaviors so that debugging and refactoring shouldn't cause problems to other parts of a project.

A good way to see if a class adheres to this principle is to say out loud the responsibilities the class has and be on the lookout for and's. Every and should go on a separate class.

Example

Let's say we have an Employee Management class that's responsible for basic CRUD operations over Employees and Logging the results on the console (👀). In this class, we have a function that pushes employees to an array and a function to log the result of the operation. The code would be something like this:

//EmployeeManagement.js

class EmployeeManagement {
  constructor() {
    this.employees = [];
  }

  addEmployee(employee) {
    this.employees.push(employee);
    this.logResult('[LOG] EMPLOYEE ADDED');
  }

  logResult(message) {
    console.log(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Suppose that you're asked to apply the following changes to the code:

  1. Add a validation so that the employee's name isn't empty.
  2. Log operations results into a file and not the console.

Let's apply change number 1. The resulting code will be the following:

//EmployeeManagement.js

class EmployeeManagement {
  constructor() {
    this.employees = [];
  }

  addEmployee(employee) {
    if (employee.name) {
      this.employees.push(employee);
      this.logResult('[LOG] EMPLOYEE ADDED');
    } else {
      this.logResult('[ERROR] NAME MUST NOT BE EMPTY');
    }
  }

  logResult(message) {
    console.log(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Validating that the employee's name is not empty is still a behavior expected from an Employee Management class. The reason to change was to improve the creation of employees.

Now let's apply change 2. The resulting code will be the following:

//EmployeeManagement.js

class EmployeeManagement {
  constructor() {
    this.employees = [];
  }

  addEmployee(employee) {
    if (employee.name) {
      this.employees.push(employee);
      this.logResult('[LOG] EMPLOYEE ADDED');
    } else {
      this.logResult('[ERROR] NAME MUST NOT BE EMPTY');
    }
  }

  logResult(message) {
    /*writeToFile is a mock function, just for educational purposes*/
    writeToFile(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Logging the result of the operation into a file is not a behavior related to Employees Management. Does it relate to CRUD operations?.

You can tell by now that the Employee Management class had two reasons to change, going against the Single Responsibility Principle. Did you notice when I explained what the class was going to do I used one "and" 👀?

Let's say we have an Employee Management class that's responsible for basic CRUD operations over Employees and Logging the results on the console.

To apply the principle, we should have the logResult function on a separate Logger class so that if we ever want to change the way we log the results, it should be on its own, separate class. The refactor of the classes should be as follows:

//EmployeeManagement.js

class EmployeeManagement {
  constructor() {
    this.employees = [];
    this.logger = new Logger();
  }

  addEmployee(employee) {
    if (employee.name) {
      this.employees.push(employee);
      this.logger.logResult('[LOG] EMPLOYEE ADDED');
    } else {
      this.logger.logResult('[ERROR] NAME MUST NOT BE EMPTY');
    }
  }
}

//Logger.js
class Logger {
  logResult(message) {
    /*writeToFile is a mock function, just for educational purposes*/
    writeToFile(message);
  }
}
Enter fullscreen mode Exit fullscreen mode

Remember that we want each class to have one and only one reason to change. With the new code, if we want to change the way we add an employee or the way we log the results of an operation we can do so in their own, separate classes, following the Single Responsibility Principle.

Discussion (0)