TypeScript Adapter Pattern Example

1. Definition

The Adapter pattern is a structural design pattern that allows objects with incompatible interfaces to collaborate. It works by encapsulating one interface (the adaptee) into another interface (the target) that clients expect.

2. Problem Statement

You may have two components in your application with incompatible interfaces. This incompatibility can arise due to components being from different libraries or if they were developed at different times with different requirements. It becomes a challenge to make them work together without modifying their source code.

3. Solution

The Adapter pattern lets you create a middle-layer class that serves as a translator between your application's interface and a legacy or third-party class. This translator is the adapter. By calling methods on the adapter, the adapter translates those calls to the adaptee's methods in a way that the adaptee expects.

4. Real-World Use Cases

1. Translating data from one format to another, like XML to JSON.

2. Adapting a new software system to use legacy modules without changing their code.

3. Integrating third-party libraries with different interfaces.

5. Implementation Steps

1. Identify the target interface that clients communicate with.

2. Create an adapter class that implements this target interface.

3. The adapter class should have a reference to the adaptee.

4. In the adapter methods, translate the method calls to calls on the adaptee.

6. Implementation in TypeScript

// Step 1: Identify the target interface
interface Target {
    request(): string;
}
// Existing class with a different interface
class Adaptee {
    specificRequest(): string {
        return "Specific result from the adaptee.";
    }
}
// Step 2: Create the adapter class
class Adapter implements Target {
    private adaptee: Adaptee;
    constructor(adaptee: Adaptee) {
        this.adaptee = adaptee;
    }
    // Step 4: Translate the method calls
    request(): string {
        return `Adapter: (TRANSLATED) ${this.adaptee.specificRequest()}`;
    }
}
// Usage:
const adaptee = new Adaptee();
console.log("Adaptee: " + adaptee.specificRequest());
const adapter = new Adapter(adaptee);
console.log(adapter.request());

Output:

Adaptee: Specific result from the adaptee.
Adapter: (TRANSLATED) Specific result from the adaptee.

Explanation:

The Adapter pattern allows a class to work with methods from another class that might not be otherwise compatible. 

In our TypeScript implementation, the Adaptee has a specificRequest method that returns a result. However, our system expects objects to follow the Target interface with a request method. 

The Adapter class bridges this gap by implementing the request method and translating its call to the specificRequest method of the Adaptee.

7. When to use?

The Adapter pattern is useful when:

1. You want to use an existing class but its interface doesn't match what you need.

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

3. You need to work with multiple existing subclasses but it's impractical to subclass each one. An adapter can adapt the interface of its parent class.


Comments