Adapter pattern
The adapter pattern wraps an incompatible third-party interface so your code can use it through an interface it already expects. Structural bridging without changing either side.
Introduction
The Adapter pattern lets a client use an existing class through an interface it already understands. It is a structural boundary that translates calls, data, and sometimes exceptions between your contract and an incompatible SDK or legacy component.
TL;DR / mental model: Keep your application talking to its own target interface, then put a thin adapter at the boundary that translates to the adaptee's API.
Problem and Context
Your checkout service charges credit cards through a PaymentProcessor interface. Every service, every test, every retry wrapper depends on that interface. Then the business signs a deal with PayPal. PayPal's SDK has completely different method names, different parameter shapes, and returns different result types.
You are stuck between two interfaces that do not match. The third-party SDK cannot be modified, and changing your interface would touch every caller. The adapter pattern gives you a third option: write a thin wrapper that translates one interface into the other.
When It Helps
Use Adapter when your code owns a stable target interface but an existing library or legacy implementation has a different API. It is especially useful when changing either side would spread vendor details through many callers.
Participants and Structure
PaymentProcessor is the target interface your code depends on. StripeGateway and PayPalClient are the adaptees, third-party classes with incompatible APIs. StripeAdapter and PayPalAdapter are the adapters, implementing the target and delegating to the adaptee. Your checkout service never knows which gateway is behind the adapter.
Idiomatic Example and Implementation Notes
The adapter is deliberately thin. Its only job is translation: map parameter types, convert result types, and delegate. No business logic lives here.
Use an object adapter (composition) as the default. It wraps the adaptee as a field, so it can adapt final classes, swap adaptees at runtime, and accept test doubles. A class adapter uses inheritance and, where supported, multiple inheritance; Java does not support multiple class inheritance, and inheritance couples the adapter to one adaptee type.
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with SDEpedia Premium.
Related Articles
The facade pattern provides a single, simplified interface to a complex subsystem with many classes. Clients call one method instead of orchestrating five.
Learn how the bridge pattern separates abstraction from implementation so both hierarchies grow independently, preventing class explosion when two axes of variation exist.
The proxy pattern wraps an object to control access, add caching, or defer creation. Virtual, protection, and caching proxies all share one trick: same interface, different control.