Learning Open-Closed Principle by Example
Published: 2021-07-20 16:38:59

Welcome to this second article in which I will show the importance of applying the [**Open-Closed Principle**](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) when implementing the code of our application.

In the same way as the previous article talking about the [Single Responsibility Principle](https://dev.to/codbugs/learning-single-responsibility-principle-by-example-593d), the **advantages** to applying this principle are: code comprehension, component reusability, increased development speed, ease of testability, etc. 

**What Open-Closed means**

The [**Open-Closed Principle**](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) is framed within the set of [**SOLID principles**](https://en.wikipedia.org/wiki/SOLID), a set of rules to guide our code in what it's called **Clean Code**. In this case, each letter represents a different principle:

* S for [**Single Responsibility Principle**](https://en.wikipedia.org/wiki/Single-responsibility_principle),
* O for [**Open/Closed Principle**](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle),
* L for [**Liskov Substitution Principle**](https://en.wikipedia.org/wiki/Liskov_substitution_principle),
* I for [**Interface Segregation Principle**](https://en.wikipedia.org/wiki/Interface_segregation_principle),
* D for [**Dependency Inversion Principle**](https://en.wikipedia.org/wiki/Dependency_inversion_principle)

This principle was described by [Bertrand Meyer](https://en.wikipedia.org/wiki/Bertrand_Meyer) in 1988, a long time ago when object-oriented programming (OOP) started to be a reality and not only theory:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_A software artifact should be open for extension but closed for modification._
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

The statement is clear, and there is no doubt about its meaning, or isn't it? We quickly think about **interfaces** and **abstract classes** because this is the way to think about extension and modification in OOP and, it is correct but **incomplete**. If we consider programming paradigms where no interface or abstract classes exist, how to apply this principle?

Some people say that parameters in functions or methods (procedures in some programming languages) are enough to agree with it, but this is not my case. Function parameters are a simple way to provide needed information to make the algorithm work. For example, if we have a method called `printCharacterSeveralTimes` probably has two parameters:
* the character to print,
* the number of times to print,

Is this something that will change the behavior of our method? I don't think so.

However, in some programming languages like javascript, we can **provide a parameter** that is a function and **get different behavior** depending on it. One of my articles explains it very well, [_A powerful and proven way to include views on list rendering_](https://dev.to/codbugs/a-powerful-and-proven-way-to-include-views-on-list-rendering-1o43). It shows how to change the way to render a collection of items - list, table, or inline - in React or create a new method to render them in a new way.

**Coding for learning**

I use the code of my [_previous article_](https://dev.to/codbugs/learning-single-responsibility-principle-by-example-593d) that prints the sequence of the Natural numbers as the starting point to show how this principle is applied.

```JavaScript
function Timer(frequency) {
  
  let intervalId = null;
  
  return {
    start: function(job) {
      intervalId = setInterval(job, frequency * 1000);
    },
    
    stop: function() {
      if(null !== intervalId) {
        clearInterval(intervalId);
      }
    }
  };
}

function PrintNaturalNumbersJob() {
  let number = 0;
  
  return {
    next: function() {
      console.log(number);
      number++;
    }
  };
}

function App() {
  let frequency = 1; // In seconds
  
  let timer = new Timer(frequency);
  let job = new PrintNaturalNumbersJob();

  timer.start(job.next);
}

new App();
```

Let's imagine that at this moment, a **new requirement** arrives: 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_As a user_
_I want to print numbers from any number to another number_
_So I will see the sequence_
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Most programmers will change the method that prints the sequence of Natural numbers, `PrintNaturalNumbersJob`. I think the best approach is to **create a new function** to cover the requirement. This function will have the same definition as the previous one, it will be a `Job` entity with a `next` method after instantiating it.

```JavaScript
function PrintNumbersStartingAtJob(initialNumber) {
  let number = initialNumber;
      
  return {
    next: function() {
      console.log(number);
      number++;
    }
  };
}
```

We could say that this new function called `PrintNumbersStartingAtJob` is an **abstraction** of the `PrintNaturalNumbersJob` function. We can substitute all of its code calling this new function and, all the tests created should still be green. It means that the old method is a **specialization** of the new one.

```JavaScript
function PrintNaturalNumbersJob() {
  return new PrintNumbersStartingAtJob(0);
}
```

But we just covered the first part of the requirement and, we need to modify our code to stop the sequence at the number specified. Here is where the [**Open-Closed Principle**](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) is applied.

Taking in mind rules like keep **high cohesion and loose coupling** in our code, we should provide a way to stop printing numbers. In our case, a solution goes through to pass a rule that evaluates the current number generated in the `Job` entity. Suddenly, a **new entity** appeared: `Rule`.

```JavaScript
function PrintNumbersStartingAtJob(initialNumber, rule) {
  let number = initialNumber;
      
  return {
    next: function() {
      console.log(number);
      if(rule) {
        rule(number);
      }
      number++;
    }
  };
}
```

You will notice the code checks for the existence of a `rule` parameter. This check allows us to keep the old function, `PrintNaturalNumbersJob`, still working if we called it in our code.

We could set that our rule needs to stop the timer when reaching a specific number. Our code for the rule is the following:

```JavaScript
function StopTimerWhenNumberIsHigherOrEqualToRule(timer, lastNumber) {
  return function(number) {
    if(number >= lastNumber) {
      timer.stop();
    }
  };
}
```

Very easy to read and to understand. In addition, very easy to test, in my opinion. The `App` entity also needs to be modified and call the proper new entities and functions.

```JavaScript
function App(startAt, endsAt) {
  let frequency = 1; // In seconds
  
  let timer = new Timer(frequency);
  let rule = new StopTimerWhenNumberIsHigherOrEqualToRule(timer, endsAt);
  let job = new PrintNumbersStartingAtJob(startAt, rule);
        
  timer.start(job.next);
}

// Our app can initiate this way to count from 5 to 10.
new App(5, 10);
```

As an example, we could **add some more rules** to our code based on new requirements just **creating new functions**: send the current number to another system, increment the number the times specified, reacting based on the number, etc. 

It seems to be easy to add more and more code without compromising our app. Newbies will be able to create the proper code without touching core functions unnecessarily. They will focus on the right part of the code.

**Wrapping up**

This article shows how to improve our code and maintainability by applying the  [**Open-Closed Principle**](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle).

It could be something complex to apply at the beginning if you are not used to it but makes you grow up on how to design the components of your app. When you delegate some activities of your method in its parameters you probably apply this principle. 

What do you think about the exercise made in this article?
Hope this can be useful to you or just have fun reading it.