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:
typescript1// FedEx API2class FedExAPI {3 createShipment(origin: FedExAddress, dest: FedExAddress): FedExShipment { }4 calculateRates(shipment: FedExShipment): FedExRate[] { }5}67// UPS API8class UPSAPI {9 ship(from: UPSLocation, to: UPSLocation): UPSTracking { }10 getRates(tracking: UPSTracking): UPSQuote { }11}1213// Your application needs a unified interface14interface 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:
typescript1class FedExAdapter implements ShippingService {2 constructor(private fedex: FedExAPI) {}34 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 }1112 private convertAddress(addr: Address): FedExAddress {13 return { /* convert format */ };14 }1516 private convertRate(rate: FedExRate): Rate {17 return { /* convert format */ };18 }19}
Pattern Structure
1┌─────────────────┐ ┌─────────────────┐2│ Client │────────►│ Target │3│ │ │ (interface) │4└─────────────────┘ └────────┬────────┘5 │6 │ implements7 ▼8 ┌─────────────────┐9 │ Adapter │10 │─────────────────│11 │ - adaptee │12 │ + request() │13 └────────┬────────┘14 │15 │ delegates to16 ▼17 ┌─────────────────┐18 │ Adaptee │19 │─────────────────│20 │ + specificReq() │21 └─────────────────┘
When to Use Adapter
- Legacy integration - Use existing classes with incompatible interfaces
- Third-party libraries - Wrap external APIs in your interface
- Future-proofing - Abstract external dependencies
- Testing - Create test adapters for external services
Object vs Class Adapter
Object Adapter (composition - preferred):
typescript1class Adapter implements Target {2 constructor(private adaptee: Adaptee) {}3 request() { this.adaptee.specificRequest(); }4}
Class Adapter (inheritance - less flexible):
typescript1class 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.