โ† Back to Design Patterns
๐Ÿ”Œ
Structural

Adapter

Wraps an incompatible interface so it can work with existing code without modification.

The Problem

You want to use a third-party library or legacy class, but its interface doesn't match what your code expects. You can't (or don't want to) modify either side.

How It Works

  • โ€บBridges two incompatible interfaces without changing their source code
  • โ€บObject adapter wraps the instance (composition); class adapter uses inheritance
  • โ€บThe client only ever sees the target interface โ€” not the adapted class
  • โ€บCommon when integrating third-party SDKs or legacy code
  • โ€บCan adapt multiple adaptees if needed (multi-adapter)

When to Use

โœ“Using a third-party library with a different interface than what your code expects
โœ“Reusing legacy code that cannot be modified
โœ“Multiple classes with incompatible interfaces need to collaborate
โœ“Providing a stable interface while underlying implementations change

Real-World Examples

๐ŸŽ
iOS

UITableViewDataSource & UICollectionViewDataSource adapting model arrays to views

๐Ÿค–
Android

RecyclerView.Adapter adapting List<T> to RecyclerView cells

๐ŸŒ
Web

Axios adapters (browser/node), ORM adapters for different databases

Code Examples

Swift
// Legacy payment SDK (cannot modify)
class LegacyPaymentSDK {
    func makePayment(amount: Double, cardNumber: String) -> Bool {
        print("Legacy: charging $\(amount) to \(cardNumber)")
        return true
    }
}

// Modern interface our app depends on
protocol PaymentProcessor {
    func process(amount: Double, method: String) -> Bool
}

// Adapter: wraps legacy to match our interface
class PaymentAdapter: PaymentProcessor {
    private let sdk = LegacyPaymentSDK()

    func process(amount: Double, method: String) -> Bool {
        sdk.makePayment(amount: amount, cardNumber: method)
    }
}

// App code only knows about PaymentProcessor
let processor: PaymentProcessor = PaymentAdapter()
processor.process(amount: 99.99, method: "4111-1111-1111-1111")