Back to the homepage
Angular

Angular & Open/Closed Principle

Introduction

This is the second part of the SOLID article series. It’s a set of principles that will not only allow us to write a code that scales more effortlessly but also change the behavior of our application without changing a large part of the application code. 

The following set of rules consists of: 

Today we’ll focus on the Open/Closed Principle 🙂

Open/Closed Principle

Credit: https://maksimivanov.com/posts/open-closed-principle/

As we can see in the picture above – poor Edward Scissorhands, only has scissors at his disposal. It would be far better if he could use other tools as well. How can it be done? Maybe he should simply take the scissors into his hands instead of attaching them to his body permanently. 🙂

Moving on to the definition of this rule: class/object should be open for expansion, but closed for modification. What does it mean? In brief – you need to write the code in such a way that new functionalities can be introduced without modifying the existing code.

Initially, it referred to the realization of polymorphism (changing the objects’ behavior) through inheritance, i.e. creating a base class and inheriting from it.

Then:

  • the base class is closed to modifications by the extending class because from the level of the extending class we have no influence on the methods of the base class,
  • the base class is open to expansion because we can extend it through inheritance and extend its functionality.

But inheritance introduces a strict dependency between objects (between the base class and its subclasses). Remember, creating a code, we should try to avoid strict dependencies. 

Otherwise:

  • we make it difficult to mock dependencies and to write tests,
  • we also make it difficult to swap implementations (it requires changes in many classes),
  • we create code that is less susceptible to change (one big dependency monolith is created – changing one thing entails changing many others). 

Therefore, it is better to use interfaces instead of inheritance. Why? 

  • we can easily change individual implementations (thus changing the behavior of the application), without changing the rest of the code,
  • interfaces are closed for modifications, but open for expansion (we change the behavior by adding a new implementation of an existing interface),
  • when using interfaces, we are not dependent on the implementation/fields defined in the base class (so we are more independent),
  • we introduce an additional layer of abstraction that allows objects to be “loosely” bound.

Example

Recently, we have been developing a new version of an existing application module. However, we didn’t want the new version to be visible to every user. We wanted to limit access to this new module for only a few trusted clients. In order to realize this, we decided to use the so-called feature flags mechanism. 

It works like this: the backend (and theoretically also the frontend) provides information which functionalities are enabled for a particular user and which aren’t. This is an object that contains multiple key-value fields. If we get a value of “TRUE” in any of the fields, the given functionality should be enabled.

To check access to the newly created module, we used the guard, which checked whether the feature flag allows us to access it or not. Therefore, we have created an enum that describes the modules, and also a website that has a method to check access to a new module:

In our guard, we call the hasAccessToNewOrderModule method and based on its result, we enable the transition to a new module or we block it. On the surface, everything seems fine – the code is working. 

So where’s the problem?

What if we would like to check the access to other modules? We would need to modify the existing service by adding another method. This way our file with this service could grow to many, many lines. Besides, either the guard would have to know all these methods, or we would have to create multiple guards, each per module, and we would have to know, which method to call. 

Certainly, some of you have already encountered files with hundreds of lines of code in your career. If not, believe me, you wouldn’t want to modify something in that kind of code afterwards 😉 To avoid this situation, let’s modify this code to a better form:

If there was a new module to which we would like to check access, it would be enough to add a line in the enum and use the new, more generic service method. 

However, this still wouldn’t be perfect – (probably) we’d have to modify the big switch that would be inside of this method. Even though we could already have one common guard that uses the generic method, we would still need to pass the appropriate enum’s value to it.

So, let’s modify this code even further and isolate the interface:

Now, for each module that we would like to check access to, we create a new implementation:

From now on, we use abstractions in our guards (not a specific implementation):

We provide a specific implementation, e.g. at the module level:

This is how we stick to the Open / Closed Principle. In the next article, we’ll look at the Liskov Substitution Principle 🙂.

About the author

Wojciech Janaszek

I am an Angular and NestJS developer. I started my adventure with the first versions of "new" Angular (2 and up). Recently I'm also using NestJS (which is easy to learn if you know Angular well - everything is very similar). In my work I pay a lot of attention to so called clean code and clean architecture. I like to have "order" in the code :) In my free time I am interested in sports cars, motorsport in general. I also play amateur volleyball.

Do you want to create articles for our blog together with us? Join us and create valuable content for Angular fans with Angular.love!

0 comments

Leave a Reply

Your email address will not be published. Required fields are marked *