Productivity is the result of a commitment to excellence, intelligent planning, and focused effort.

JavaScript Adapter Pattern

Cover Image for JavaScript Adapter Pattern
George Crisan
George Crisan
Posted on:

In this tutorial we have an example of how to use the "Adapter Pattern" design pattern with JavaScript. We have two APIs that do the same thing, reversing a string and returning a custom output, as upper case. ReverseV1 and ReverseV2.

You could think of ReverseV1 as the legacy API which has to be replaced gradually. The ReverseV2 API is more modular and gives to the developer more flexibility.

The problem the "Adapter Pattern" solves, is the ability to provide the new API to applications that rely on the old API interface, without breaking the functionality and most important without the need to modify the application eveywhere where our method has been used.

If this does not make sense, it may be useful to have a look over some examples. The code written below is functional but is only a proof of concept, a simplified version of what a real life interface could be.

The old API

The main method, "reverseAndUpperCase" has been use in our applications in many places.

Our next task is to replace the old deprecated api and to replace it with the new one.

  class ReverseV1 {
    constructor(word) {
      this.word = word;
    }

    reverseAndUpperCase(){
      let transformed = `Result: ${[...this.word].reverse().join("").toUpperCase()}`;
      return transformed;
    }
  }

  let reverseFirst = new ReverseV1('house');

  console.log('Test 1:', reverseFirst.reverseAndUpperCase());
  // Output: Test 1: Result: ESUOH

The new API

ReverseV2 can do the same thing as the old API but has more methods available, which gives more flexibility and more functionality to the developer. (An alternative Api could be more sofisticated, more performant or having a better library)

class ReverseV2 {
    constructor(value){
      this.wordToTransform = value;
    }

    reverseWord() {
      return [...this.wordToTransform].reverse().join("");
    } 

    uppercaseWordAfterReverse() {
      return this.reverseWord().toUpperCase();
    }

    outputUpperReversedString() {
      return `Result: ${this.uppercaseWordAfterReverse()}`;
    }

    outputDefaultValue() {
      return `Result: ${this.wordToTransform}`;
    }

    reverseAndUpperCase() {
      return this.outputUpperReversedString();
    }
  }

  const reverseSecond = new ReverseV2('consciousness');

  console.log('Test 2: ', reverseSecond.reverseAndUpperCase());
  //Output: Test 2:  Result: SSENSUOICSNOC

The Adapter

In order to be able to replace our old API with the latest version we could take advantage of the "Adapter Pattern".

Adapter pattern is a structural pattern where the interface of one class is translated into another. This pattern allows classes work together that could not otherwise, because of incompatible interfaces.

  class ReverseAdapter {
    constructor(word, api) {
      this.selectedAPI = new api(word);
      this.reverseAndUpperCase = function() {
        return this.selectedAPI.reverseAndUpperCase();
      }
    }
  }

  let reverseAdaptedV1 = new ReverseAdapter('house with new api', ReverseV1);
  let reverseAdaptedV2 = new ReverseAdapter('house with new api', ReverseV2);

  console.log('Test 3: ', reverseAdaptedV1.reverseAndUpperCase());
  console.log('Test 3.1: ', reverseAdaptedV2.reverseAndUpperCase());
  //Output:Test 3:  Result: IPA WEN HTIW ESUOH
  //Output:Test 3.1:  Result: IPA WEN HTIW ESUOH

As you can see, using the Adapter will allow the method "reverseAndUpperCase" to use the new API.

As I have mentioned at the beggining of the article, this is not an example of the best practices but rather a proof of concept over the "Adapter Pattern".

Thank you.