20 minlesson

Introduction to Adapter Pattern

Introduction to Adapter Pattern

Adapter converts the interface of a class into another interface that clients expect. It lets classes work together that couldn't otherwise due to incompatible interfaces.

The Problem

Different carrier APIs have different interfaces:

typescript
1// FedEx API
2class FedExAPI {
3 createShipment(origin: FedExAddress, dest: FedExAddress): FedExShipment { }
4 calculateRates(shipment: FedExShipment): FedExRate[] { }
5}
6
7// UPS API
8class UPSAPI {
9 ship(from: UPSLocation, to: UPSLocation): UPSTracking { }
10 getRates(tracking: UPSTracking): UPSQuote { }
11}
12
13// Your application needs a unified interface
14interface ShippingService {
15 getRate(from: Address, to: Address, weight: number): Rate;
16 createLabel(shipment: Shipment): Label;
17}

The Solution: Adapter

Create adapters that wrap each API:

typescript
1class FedExAdapter implements ShippingService {
2 constructor(private fedex: FedExAPI) {}
3
4 getRate(from: Address, to: Address, weight: number): Rate {
5 const fedexFrom = this.convertAddress(from);
6 const fedexTo = this.convertAddress(to);
7 const shipment = this.fedex.createShipment(fedexFrom, fedexTo);
8 const rates = this.fedex.calculateRates(shipment);
9 return this.convertRate(rates[0]);
10 }
11
12 private convertAddress(addr: Address): FedExAddress {
13 return { /* convert format */ };
14 }
15
16 private convertRate(rate: FedExRate): Rate {
17 return { /* convert format */ };
18 }
19}

Pattern Structure

1┌─────────────────┐ ┌─────────────────┐
2│ Client │────────►│ Target │
3│ │ │ (interface) │
4└─────────────────┘ └────────┬────────┘
5
6 │ implements
7
8 ┌─────────────────┐
9 │ Adapter │
10 │─────────────────│
11 │ - adaptee │
12 │ + request() │
13 └────────┬────────┘
14
15 │ delegates to
16
17 ┌─────────────────┐
18 │ Adaptee │
19 │─────────────────│
20 │ + specificReq() │
21 └─────────────────┘

When to Use Adapter

  1. Legacy integration - Use existing classes with incompatible interfaces
  2. Third-party libraries - Wrap external APIs in your interface
  3. Future-proofing - Abstract external dependencies
  4. Testing - Create test adapters for external services

Object vs Class Adapter

Object Adapter (composition - preferred):

typescript
1class Adapter implements Target {
2 constructor(private adaptee: Adaptee) {}
3 request() { this.adaptee.specificRequest(); }
4}

Class Adapter (inheritance - less flexible):

typescript
1class Adapter extends Adaptee implements Target {
2 request() { this.specificRequest(); }
3}

Summary

Adapter is the go-to pattern for integrating incompatible interfaces. Use object adapters for flexibility and testability.