Adapter Design Pattern in JavaScript

1. Definition

The Adapter Design Pattern allows classes with incompatible interfaces to work together. It acts as a bridge between two incompatible interfaces by converting the interface of one class into another interface that the client expects.

2. Problem Statement

Imagine you've developed a system that uses a certain class with a specific interface. Later, you decide to use a third-party library, and you realize that this library does exactly what you need but exposes a different interface. You can't alter the third-party code, so how do you make your current system and the third-party library work together without changing either?

3. Solution

The Adapter pattern comes to the rescue. By introducing an additional layer - the adapter - you can translate one interface to another. The adapter wraps the adaptee (in this case, the third-party library or class) and exposes a new interface that matches what the client expects.

4. Real-World Use Cases

1. Memory card adapters that allow micro SD cards to be read by SD card slots.

2. Power plug travel adapters that allow you to use your devices in countries with different socket shapes.

3. Middleware in web servers that adapt different web request formats to a uniform format your application can handle.

5. Implementation Steps

1. Identify the interfaces that are incompatible.

2. Create an adapter class that wraps the adaptee.

3. Implement the desired interface in the adapter.

4. The adapter class should delegate the requests from the client to the adaptee in a manner that the adaptee understands.

6. Implementation in JavaScript

// Third-party library (Adaptee)
function ThirdPartyLogger() {
  this.logMessage = function(message) {
    console.log("Third party: " + message);
  };
}
// Desired interface
function Logger() {
  this.log = function(message) {};
}
// Adapter
function LoggerAdapter(thirdPartyLogger) {
  this.thirdPartyLogger = thirdPartyLogger;
  this.log = function(message) {
    this.thirdPartyLogger.logMessage(message);
  };
}
// Client code
const myLogger = new LoggerAdapter(new ThirdPartyLogger());
myLogger.log("Hello Adapter!");

Output:

Third party: Hello Adapter!

Explanation:

1. ThirdPartyLogger is the adaptee – a class with a method logMessage that our system doesn't directly support.

2. Logger is our desired interface with a simple log method.

3. LoggerAdapter acts as an adapter, wrapping around the ThirdPartyLogger. It takes an instance of ThirdPartyLogger and implements the log method, which internally calls the logMessage method of the ThirdPartyLogger.

4. The client code uses LoggerAdapter as if it were a standard Logger, unaware of the adaptation happening behind the scenes.

7. When to use?

Use the Adapter pattern when:

1. You want to use an existing class but its interface doesn't match the one you require.

2. You want to create a reusable class that cooperates with classes with incompatible interfaces.

3. You want to combine multiple libraries or APIs which have different interfaces, into a unified application.


Comments