Open-Closed Principle - SOLID Principles in Javascript

Open-Closed Principle - SOLID Principles in Javascript

Learn about the Open-Closed Design Principle to write clean code.

Play this article

This is the second article in our series of articles covering the SOLID Design Principles.

If you are unaware of SOLID, you can read the first article where I gave a brief introduction to SOLID. In this article, we will be covering the Open-Closed Principle.

What is the Open-Closed Principle?

The Open-Closed principle states that entities are open for extension but closed for modifications. Now, this definition obviously doesn't make sense to anyone. So let's understand it by some alternative and simpler definitions:

  • You can create new entities but you cannot change the existing ones.
  • You can add new code (that will work with the old code) but you cannot change the old code.

Here, the word entity refers to an object, class, or function or in other words a set of instructions grouped together. So basically, you cannot change the existing set of instructions but you can add new ones. Let's understand it with an example:

Example

Consider we have a set of users and we want to sort them in different ways and in different orders.

const users = [
  {
    name: "Shahmir",
    age: 19,
  },
  {
    name: "Faisal",
    age: 18,
  },
  {
    name: "Usman",
    age: 20,
  },
];

Here each user has a name and an age property and we want to sort these using name and age and in ascending and descending order.

Naive Solution

We can sort them using a function like this:

const sortUsers = (users, sortBy, order) => {
  switch (sortBy) {
    case "name":
      switch (order) {
        case "asc":
          users = users.sort((user1, user2) =>
            user1.name < user2.name ? -1 : 1
          );
          break;

        case "desc":
          users = users.sort((user1, user2) =>
            user1.name > user2.name ? -1 : 1
          );
          break;
      }
      break;

    case "age":
      switch (order) {
        case "asc":
          users = users.sort((user1, user2) => user1.age - user2.age);
          break;

        case "desc":
          users = users.sort((user1, user2) => user2.age - user1.age);
          break;
      }
      break;
  }

  return users;
};

This function takes the users, orderBy(name/age) and order(ascending/descending) as arguments. And uses a bunch of switch statements to sort them in different ways using the sort function. Now, this solution works but it's violating the Open-Closed principle.

Why is it Violating the Open-Closed Principle?

The above solution is violating the Open-Closed principle and it is not the best way to perform this task. What if we have first name and last name and we want to sort using these in ascending/descending order. We need to modify our existing entity(sortUsers function) and add more switch statements to make it work and the principle states that we should not modify existing entities.

Better Solution

Instead of changing the existing entity, we want to be able to add new ones. So let's change the above code to make it work better.

class SortUserName {
  constructors(users) {
    this.users = users;
  }

  ascending() {
    return users.sort((user1, user2) => (user1.name < user2.name ? -1 : 1));
  }

  descending() {
    return users.sort((user1, user2) => (user1.name > user2.name ? -1 : 1));
  }
}

class SortUserAge {
  constructors(users) {
    this.users = users;
  }

  ascending() {
    return users.sort((user1, user2) => user1.age - user2.age);
  }

  descending() {
    return users.sort((user1, user2) => user2.age - user1.age);
  }
}

In the above code, we created 2 classes one for sorting through name and the other for sorting through age. And both of them have ascending and descending functions for sorting in different orders. Now, this code is a lot better than the previous one.

Why is it a Better Solution?

If we want to sort them using first name or last name, all we need to do is to create new classes for them and this is what Open-Closed principle says(create new entities).

Here's how we can use the newly created classes:

const sort = new SortUserAge(users);
console.log(sort.ascending());

Now, we don't need to modify any existing code. Whenever we need to change how we are sorting the users, we can just create a new class for it without changing any existing classes.

So that's it. Comment below if you have any questions. Will see you in the next article covering the Liskov Substitution principle.

Follow me on:
Twitter
LinkedIn
Github