Design Patterns: Adapter Pattern in TypeScript

Tram Ho

Easily solve interface incompatibility issues using the Adapter Pattern

Design Patterns are very important for Web Developers and we can code better by mastering them. In this article, I will use TypeScript to introduce the Adapter Pattern.

Common scenario

In the web system, mail service is a very commonly used service. On the Node.js platform, we can use the nodemailer module to easily implement email sending functionality. After successfully installing nodemailer module, you can follow below steps to send email:

To avoid binding the mail service to a specific service provider , before developing the mail service , we define the interface related to the mail provider :

With these interfaces, we can easily create a mail service :

For now, this solution is not without problems, but if one day we need to use a third-party email cloud service provider . Such as sendgrid or mailersend , etc. You will see that the name of the function that the SDK uses to send mail is send . So we go ahead and define an Interface CloudEmailProvider :

Comparing the previously defined Interface EmailProvider , you will see the following problem:

Screenshot 2022-12-02 at 23.31.59.png

Therefore, we cannot directly use EmailService to access third-party email cloud service providers . To solve this problem, there are many ways. Let’s see how to use the Adapter Pattern to solve the above problem.

Adapter Pattern

The purpose of the Adapter Pattern is to allow two objects to not work together because the Interface does not match. It’s like glue, transforming different things so they can work together. Or the easiest to imagine is when you come to Japan all sockets are flat ends => we need a corresponding Adapter to handle this.

In the example above, the Adapter Pattern contains the following roles:

  • Client(EmailService) : The object that needs to use the Target interface;
  • Target(EmailProvider) : Defines the Interface that the Client expects;
  • Adapter(CloudEmailAdapter) : Adjust Adapter Interface to Target interface;
  • Adapter(CloudEmailProvider) : Defines the Interface that needs to be adjusted.

After going through some of the work required to apply the Adapter Pattern to this example, let’s first create CloudEmailAdapter class:

In the above code, because the two Interfaces of EmailProvider and CloudEmailProvider do not match, we create a class CloudEmailAdapter to solve the compatibility issue.

Next, we take the sendgrid service as an example to implement SendgridEmailProvider :

Hint: The above code is just to show you how to use the Apply Pattern and needs to be adjusted accordingly when used in real projects.

Now that the SendgridEmailProvider and CloudEmailAdapter classes are defined, let’s see how to use them:

=> Now, no matter what service, you can use the sendMail function.

External example: Computers often have an Adapter sac => via Japan 110V or back to VN 220V … as long as there is a charging Adapter, everything is the same, just perform the action of plugging it into an electrical outlet.

Finally, the usage scenarios of the Adapter Pattern:

  • The system needs to use an existing class and the interface of this class does not meet the system’s needs, that is, the interface is not compatible;
  • Use a service provided by a third party, but the interface definition of this service is different from your interface definition.


As always, I hope you enjoyed this article and learned something new.

Thank you and see you in the next posts!

If you find this blog interesting, please give me a like and subscribe to support me. Thank you.


Share the news now

Source : Viblo