STRUCTURAL PATTERNS / OBJECT DESIGN BRIEF

Adapter pattern

The Adapter pattern converts the interface of an existing class into the interface a client expects, so classes with incompatible interfaces can work together.

BeginnerPhase 05 / Topic 1 of 7ResponsibilitiesCollaborationsExtensibility
01

Overview

The Adapter pattern converts the interface of an existing class into the interface a client expects, so classes with incompatible interfaces can work together. Your code expects PaymentGateway.charge(amount), but the vendor SDK offers createTransaction({ value, currency, metadata }); an adapter translates between them.

Adapters are the standard way to integrate third-party libraries, legacy code, and external APIs without spreading their types through your codebase. Combined with DIP, they form the 'adapters' in ports-and-adapters architecture.

A travel plug adapter

Your laptop charger has a US plug; the wall socket is European. The adapter changes the shape without changing the charger or the wall.

02

When to use it

  • Integrating a vendor SDK or legacy class with a different interface.
  • Supporting several providers behind one interface.
  • Isolating your code from external API changes.
03

Where it shows up in interviews

Third-party integration

Recognize it when: vendor API differs from your interface.

  • Design a payment gateway with multiple providers
  • Design a notification service with Twilio and SES
Legacy integration

Recognize it when: reuse old code in a new design.

  • Integrate a legacy inventory system
  • Wrap an XML API as JSON
04

Where it is used in real software

Java InputStreamReader

Adapts a byte-based InputStream to a character-based Reader.

Arrays.asList

Adapts an array to the List interface.

Hexagonal architecture

Adapters implement domain ports for databases, message brokers, and HTTP clients.

05

Key terms

Target
The interface the client expects.
Adaptee
The existing class with an incompatible interface.
Adapter
Implements the target and delegates to the adaptee.
Object vs class adapter
Composition (preferred) vs inheritance.
06

How it works, step by step

  1. 1
    Define the target interface

    What your domain needs.

  2. 2
    Wrap the adaptee

    Hold it as a field.

  3. 3
    Translate calls

    Map parameters, units, and names.

  4. 4
    Translate results and errors

    Return domain types and exceptions.

  5. 5
    Inject the adapter

    Clients only see the target.

07

Payment provider adapters

Target: charge(customerId, amountCents): ChargeResult

Step 1 / 3
AdapteeNative callAdapter translates
Stripe SDKpaymentIntents.create({amount, currency, customer})cents directly, maps status
Legacy bank APIdebit(accountNo, dollars as string)cents to '12.34', account lookup
PayPal SDKorders.capture(orderId)creates then captures, maps errors

NOWAdaptee: Stripe SDK | Native call: paymentIntents.create({amount, currency, customer}) | Adapter translates: cents directly, maps status

Business code calls one interface; each provider's quirks stay in its adapter.

08

Implementation

// Target interface owned by our domaininterface PaymentGateway {  charge(customerId: string, amountCents: number): Promise<{ ok: boolean; reference: string }>;} // Adaptee: legacy client with a different shapeclass LegacyBankClient {  async debit(account: string, amount: string): Promise<{ code: "00" | "51" | "91"; txn: string }> {    return { code: "00", txn: "TX-" + account + "-" + amount };  }} class LegacyBankAdapter implements PaymentGateway {  constructor(private bank: LegacyBankClient, private accounts: Map<string, string>) {}   async charge(customerId: string, amountCents: number) {    const account = this.accounts.get(customerId);    if (!account) throw new Error("No bank account on file");    const res = await this.bank.debit(account, (amountCents / 100).toFixed(2)); // unit conversion    return { ok: res.code === "00", reference: res.txn };                      // result mapping  }}
09

Complexity and performance

Overhead1 delegation + mapping

Negligible.

Classes1 adapter per adaptee

Isolates each vendor.

10

Trade-offs

Isolation vs lost features

A common target interface may hide provider-specific capabilities.

Extra layer

Adds a class, but prevents vendor types from spreading.

11

Variants and related techniques

Two-way adapter

Implements both interfaces.

Anti-corruption layer

DDD term for a larger adapter translating an entire external model.

12

Common mistakes

  • Leaking adaptee exceptions or types.

    Fix: Translate them to domain types.

  • Adding business logic to adapters.

    Fix: Adapters translate; rules belong in the domain.

13

Interview questions

Adapter vs Facade?

Adapter makes an existing interface match a different expected interface. Facade provides a new simplified interface over a complex subsystem. Adapter changes shape; facade simplifies.

Adapter vs Decorator?

Adapter changes the interface; Decorator keeps the same interface and adds behavior.

14

Practice problems

ProblemDifficultyWhat it trains
Adapt an XML weather API to a JSON interfaceEasyTranslation.
Support Stripe and PayPal behind one gatewayMediumResult and error mapping.