loading...
Cover image for Applying Open-Closed Principle with Decorator Pattern in Typescript

Applying Open-Closed Principle with Decorator Pattern in Typescript

joaosczip profile image joaosczip ・4 min read

That's the first time that I'm writing a post in english, so any feedback that you may give to me will be very important. I'll be grateful.

Preface

In the recent days some of my study focus are the SOLID principles, Clean Architecture and some releated topics like Design Patterns, and I need to confess that I was using the OOP techiniques in the wrong way since college, I realised that what I was writing was just procedural code separated in different files with classes and methods, there wasn't a single advantage by using OOP like that, so I decided to change.

Design Principles

Every paradigm has its own principles, in the OOP world those principles are the SOLID (there are a few more, but those are the most famous and used). So what this means, these SOLID keywords? They stand for:

  • Single Responsability;
  • Open-Closed;
  • Liskov Substitution;
  • Interface Segregation;
  • Dependency Inversion;

Basically, if you build your application following all those principles, your codebase will be more flexible, abstracted and maintainable, the evolution of the software will be less painful and costly, giving you more time to expend implementing new things.

Open-Closed Principle

The Open-Closed Principle says that our classes must be open for expansions and closed for changes. Basically, we must be able to change the implementation behavior on execution-time, and to reach that, we don't need to change the class codebase, we just need the help of composition.

There is a famous dictation that says that we need to prefer composition over inheritance, and this is really important. There are some issues when we decide to use inheritance, the first is that we're breaking the OOP foundation of encapsulation, because the children knows everything about the parent. The second is the static inheritance, we can't change the behavior of the children even in execution-time, we need to change the codebase itself to be able to change the behavior, breaking the Open-Closed Principle.

When we have composition, we no longer have the "is" relationship (Ex: SavingsAccount is an Account) and we passed to have the "has" relationship (Ex: AuthorizationClient has a HttpClient), so, following the example, AuthorizationClient behaves like a normal HttpClient, but he can change your default behavior, adding an authorization header for example.

Example

Imagine the following scenario, let's say we have a front-end client application written in React, we're consuming an API and we need to pass an authentication token (a jwt for example). We decide to create an interface responsible for send HTTP requests, so, in the data layer we create our HttpPostClient protocol (Only POST requests, following the Interface Segregation principle, subject for another post).

Alt Text

After that, we create an implementation for this protocol based in the axios library.

Alt Text

Now we have our protocol (HttpPostClient) and our implementation (AxiosHttpClient), why we can't just pass the authorization header normally in the method call? We need to think that this header needs to be passed in many requests and will be always the same: Capture the token from the localStorage or from another service and pass to the method that will do the request. If we just copy & paste this implementation, we will be breaking the DRY (Don't Repeat Yourself) principle, so we need to think a smart way to do that. That's where the Decorator Pattern comes in.

The Decorator Pattern is basically a wrapper for an object. This wrapper must have the same type of the wrapped object i.e. implement the same interface and because of that, they can be exchanged in a way that the client class don't notice that change (Liskov Substitution).

The goal of this pattern is to add a behavior to the decorated object.

Going back to our example, we need a Decorator that implements the HttpPostClient interface and add the desired behavior to our AxiosHttpClient without changing the class implementation.

This Decorator class will be called AuthorizationHttpPostClientDecorator.

Alt Text

Some important things to notice:

  • Our Decorator class has the same interface of the decorated object.
  • He receives the wrapped object on the constructor (Dependency Inversion principle)
  • Runs some logic and them calls the method from the decorated object

That's an example of how we can add behavior to an class without change your implementation code.

Conclusion

Whenever we choose a new program paradigm to work with, we need to be aware of his foundations and principles, only with that knowledge we will be able to deeply understand how to use that paradigm correctly and get the most of it.

In this example I tried to demonstrate the Open-Closed principle in an easy way, with an easy example, so that you can realize its importance. I choose the decorator pattern because your application of the Open-Closed is one of the most common, however I could also implement the Proxy Pattern, he is also an structural one and work in a very similar way.

Posted on by:

joaosczip profile

joaosczip

@joaosczip

Passionate about web development and some software engineering techniques

Discussion

markdown guide
 

This is a very common (and useful!) pattern, indeed. And you summarized it pretty well 👏.

I'd like to add one point. Do you really name your class AuthorizationHttpPostClientDecorator? or was that just for educational purposes?
I ask because I frequently see pattern implementations in the wild being named after the patterns (instead of using proper names according to WHAT, as in this case, is being decorated and WHY).

To me, it feels much more natural to call this class something along the lines of AuthorizedHttpPostClient or AuthHttpPostClient, which would hint at the purpose of the base class, while the decorator is just a means to an end.

 

Yeah, it's just for educational purposes!
I don't like give to a class a very extensive name like that, unless it be inevitable.
But now I see that your tip is also needed for educational purposes too, I'll adopt it for the nexts posts.

Thank you for your feedback, comments like that help me improve a lot.

 

It's been a long time since I've seen an article which made me think like "ok let me go back and read again". And it's very rare to read it at that same instant. You've made it possible. Wonderful stuff.
Reminds me of how java forced people to use all of these things implicitly.

Cheers 👏👏👏

 

Wooow, I really don't know what to say!! I really apreciate that, thank you so much!!
I'm starting to writting posts recently, your feedback inspire me to continue with that, thanks again.

 

I have read alot of examples on these principles but your short article made it really clear, great work keep it up!

 

In the first moment I thought write just about the Open-Closed.
But then I realised that I need something more practical, so I decide to put the Decorator too.
Because these two topics are very extensive I needed to take the short way.

Thank's for your feedback, it's very important to me!!