Understanding Single responsibility principle by Javascript - [Sharing knowledge]
Tags: sharingknowledge, srp, javascript, solid
Published: 2023-02-25 09:24:45

I believe someone like me who does not know what are SOLID principles in design software? So badly, I am not learning at university or a degree. But luckily, I do self-learning information technology by myself. Until now, I am learning the SOLID principle to get cool skills or awesome knowledge about designing the application system - I think. Here are a few things about the Single responsibility principle (SRP) in the programming language. I use Javascript to code the example. Hope, you can archive something from my post.

## What will you read?

1. What is Single-Responsibility Principle?
2. Why is Single-Responsibility Principle?
3. When we use Single-Responsibility Principle?
4. Example code by Javascript.
5. Summary.

## 1. What is Single-Responsibility Principle - SRP?

SRP is a software design principle that states that a software component such as a class, an object, or a module must have only one responsibility and only one reason to change.

In other words, a class or module should have only one primary purpose. All the methods and attributes should be related to that responsibility.

It helps to reduce complexity or coupling between modules or classes. Making the code easier to understand, read or maintain and making it more reusable.

SRP is a part of the SOLID principles of object-oriented design.

SRP was first introduced in 2003 in the book “Agile Software Development, Principles, Patterns, and Practices”  by Robert C. Martin (a well-known software engineer) author.

## 2. Why is Single-Responsibility Principle?

We ask ourselves, why do we need to follow this principle in the design system? In my experience, I am working on a lot of projects. A project has a few people to a big project has 20 or more people working together. In that case, we will meet the problems about the scaleable, complex business requirement, maintaining, fixing bugs, i.e. So in this case, the principle is powerful to control the structure of the system to ensure every gear is working smoothly in a big machine. That is why we talk about a principle - SRP. Here are several important reasons we need to follow in SRP:

- Encourages modularity: by having a single purpose per module or component. The system is easier to understand, read or maintain, fixing bugs without impacting or affecting other parts of the system.
- Improves maintainability: because each module has a single responsibility so there is only one reason to change that makes the modules less affecting to the other parts of the system.
- Promotes reusability: modules only have one purpose so it’s easier to reuse. Because they are only focused on a specific task and do not have any coupling or complexity dependencies.
- Reduces coupling: if the module or component has a lot of dependencies. it makes the application harder to change or replace. So keep the module simple as we can.

## 3. When we use Single-Responsibility Principle?

SRP can be applied whenever we are designing or refactoring software components or modules. It is useful in object-oriented programming where the classes are the primary building blocks of the system. We can use the SRP in the following scenarios:

1. Designing new modules: by creating a new module, we should consider making sure that it has a single purpose to do one thing - well-defined responsibility. 
2. Refactoring existing modules: when we are refactoring the existing modules. Consider the complexity of coupling dependencies of the module. If the module has more than 2 purposes. We should break them into small modules that have a well-defined responsibility and do not overlap with other modules.
3. Design software architecture: when we design the architecture of a software system. We can use the SRP to ensure that each module has a well-defined responsibility and does not overlap other modules.
4. Writing tests: writing test is a part of software development. We can use the SRP to ensure that each test focuses on a single aspect of the module logic or behavior.

## 4. Example code by Javascript.

We look at the example below:

```jsx
function Question(questions) {
  this.questions = questions;

  this.printReport = () => {
    console.log("Printer called");
    console.log("3th party printer function called");
  };

  this.saveDatabase = (questions) => {
    console.log({questions});
    console.log(`Saved to database`);
  };

  this.remove = (question) => {
    console.log("removed question " + question);
    this.questions = this.questions.filter(q => q !== question);
    this.saveDatabase(this.questions);
  }

  this.add = (question) => {
    console.log("added " + question);
    this.questions.push(question);
		this.saveDatabase(this.questions);
  }
}

const q = new Question([
  "Question 1",
  "Question 2",
  "Question 3",
  "Question 4"
]);

q.remove("Question 1");
q.printReport();
q.add("Question 5");
q.printReport();

```

The purpose of `Question` object is to process the questions as save, list out, remove question and also printing. In this approach, the problem is the `printReport`, `saveDatabase` , `remove` function, and also the mechanism log of the system - `console.log`.

In this context, we change the method `saveDatabase` from Postgres to MongoDB, so we are going to change all the functions of objects reference to save the data method. The `printReport` , `remove` and `console.log` are the same like that.

Now, look at the new code below:

```jsx
function printer(data) {
	console.log("3th party function called");
  log(`[${new Date().toLocaleString()}]--------------------------------`);
  log(data, "info");
}

function log(message, type = "log") {
  console?.[type](message);
}

function saveDatabase(data) {
  log({data}, "info");
  log(`Saved data to database`, "info");
}

function Question(questions) {
  this.questions = questions;

  this.printReport = () => {
    printer(this.questions);
  };

  this.remove = (question) => {
    log("removed question " + question);
    this.questions = this.questions.filter(q => q !== question);
    saveDatabase(this.questions);
  }

  this.add = (question) => {
    log("added " + question);
    this.questions.push(question);
		saveDatabase(this.questions);
  }
}

const q = new Question([
	"Question 1",
	"Question 2",
	"Question 3",
	"Question 4"
]);

q.remove("Question 1");
q.printReport();
q.add("Question 5");
q.printReport();

```

The `printReport` , `saveDatabase` , `log` functions moved to other objects, implementing the `remove` method. So now every function just does one thing. If each function changes the logic, we only go to this function and do/change logic based on another requirement without modifying other functions which used them and prevent the unexpected bug.

## Summary

There are some important keywords related to Single-Responsibility Principle (SRP):

1. Responsibility: well-defined responsibility. One module - one purpose - one reason to change.
2. Cohesion: make the design system a low cohesive module that is unrelated.
3. Coupling: make the modules low coupling that is the module is not dependent on other modules.
4. Separation of Concerns: break the big module into small modules. Each module is resolved by a small part of the system. 
5. Refactoring: by breaking application to the small peace. Refactoring is easier to understand, read and maintain the module without impacting or affecting other modules.