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
↓ PaymentGateway
↓ wraps
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)
StripeAdapter
implements pay()
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.Back to all topics →