Adapter
The problem
The shop’s checkout is written against a clean CardPayments interface: charge(customer, amount)
with BigDecimal pounds and a meaningful result. The payment provider it must actually use is a
legacy gateway taking "CUST/<id>" reference strings and amounts in pence, returning magic integer
status codes. Neither side can change: the gateway is third-party, and rewriting checkout around
its quirks would spread STATUS_* constants through the whole codebase.
The pattern
An adapter implements the interface the client wants and translates every call onto the interface the legacy class provides:
CardPayments payments = new LegacyGatewayAdapter(new LegacyPaymentGateway());
PaymentResult result = payments.charge(alice, new BigDecimal("19.99")); // pounds in, meaning out
Benefits
- Incompatible APIs cooperate without modifying either side – essential when one side is third-party or frozen.
- The mess is quarantined – pence conversion and status-code decoding exist in exactly one class; the rest of the codebase speaks the domain language.
- Swappable integrations – when the shop migrates to a modern provider, only a new adapter is written; checkout code doesn’t change.
Seen in the wild
InputStreamReader (adapts InputStream -> Reader), Arrays.asList() (array -> List),
Collections.enumeration(), Spring MVC’s HandlerAdapter.
Implementation
CardPayments– the clean interface checkout is written against.LegacyPaymentGateway– the frozen third-party API with pence and status codes.LegacyGatewayAdapter– the adapter translating between them.PaymentResult– the meaningful result type.