โ 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")