design·lab

Structural pattern

Adapter

An adapter wraps an incompatible interface so your app can keep talking to the shape it already expects.

✗ The problem

Your app and the third-party SDK don't speak the same shape

Your code expects one interface. The vendor's library exposes a different one — and you can't edit their code.

// your app code expects:
interface PaymentGateway {
  pay(cents)
}

// but the 3rd-party lib gives you:
class StripeSDK {
  makeCharge(dollars, currency) { ... }
}

checkout.pay(2500);  // ✗ no pay() on StripeSDK
App
expects PaymentGateway
StripeSDK
wrong shape
Sprinkling if (isStripe) ... through the app couples you to every vendor forever.
✓ The pattern

Write a translator that implements YOUR interface

The adapter implements PaymentGateway and, under the hood, calls the adaptee with the shape it needs. The app never knows the adaptee exists.

class StripeAdapter {
  constructor(sdk) { this.sdk = sdk; }

  pay(cents) {
    // translate OUR shape → THEIR shape
    const dollars = cents / 100;
    return this.sdk.makeCharge(dollars, 'usd');
  }
}
App
calls pay(cents)
↓ PaymentGateway
StripeAdapter
implements pay()
↓ wraps
StripeSDK
makeCharge(dollars, cur)
✓ See it live

Trace a call through the adapter

Pick a vendor and watch pay(2500) flow through the adapter, which translates cents into that vendor's own units before forwarding the call.

The App's call pay(2500) is identical for both vendors — only the adapter behind it differs.

"One adapter per SDK — wasteful?" No — each is tiny, isolating one integration (SRP). Swapping vendors changes zero app code (Open/Closed + Dependency Inversion); scattered if (isStripe) … checks are the real smell.
✓ Takeaway

Isolate the translation, not the behavior

  • Integrates incompatible code — legacy modules, third-party SDKs, or ORM drivers — without changing them or your app.
  • One place to translate — the shape mismatch lives only inside the adapter.
  • Swapping vendors is cheap — write a new adapter, keep the app's interface fixed.
  • You already use it: wrapping payment SDKs, database drivers, browser polyfills.
  • Careful: an adapter only translates shape — it can't add a capability the adaptee doesn't have.
  • Not the same as: Decorator adds behavior while keeping the same interface; Facade simplifies a whole subsystem behind one entry point.
🎯 Principle applied: Adapter applies Dependency Inversion — your app depends on your PaymentGateway interface, never a vendor SDK — and Open/Closed: a new vendor is a new adapter with zero changes to app code.