Skip to main content

Adapter

Adapter converts an existing class's interface into the one clients expect, letting incompatible code collaborate. It enables reuse and isolates external APIs at the cost of an extra layer.

Type
Structural
When to Use
Incompatible Interfaces, Integrate Legacy Code, Wrap Third Party API, Reuse Existing Class

The Adapter pattern converts one interface into another that clients expect, allowing classes with incompatible interfaces to work together. It is the software equivalent of a plug adapter: the underlying device is unchanged, but a wrapper makes it fit a different socket. Adapter is the go-to solution when you must reuse an existing class — legacy code, a third-party library, or a system you cannot modify — whose interface does not match what your code requires.

How It Works

The client expects a Target interface. The Adaptee is the existing class with a useful but incompatible interface. The Adapter implements the Target and holds (or extends) the Adaptee, translating each Target call into one or more Adaptee calls, converting parameters and results as needed.

There are two forms. The object adapter composes the adaptee (holds a reference) and delegates to it — the more flexible and widely used form, since it can adapt subclasses and works in languages without multiple inheritance. The class adapter inherits from both the target and the adaptee, available only where multiple inheritance or interface default methods allow it.

When to Use It

Use an adapter when you want to use an existing class whose interface does not match your need, when integrating with third-party or legacy APIs you cannot change, or when you want a stable internal interface insulated from external libraries so swapping a vendor later touches only the adapter. It is fundamental to ports-and-adapters (hexagonal) architecture.

Do not use it to paper over a poorly designed interface you do control — fix the source instead. And an adapter only translates shape; it cannot add behavior the adaptee lacks.

Trade-offs

Adapters enable reuse of otherwise incompatible code and isolate the rest of the system from external interfaces, which improves maintainability and makes vendor substitution cheap. They uphold the single-responsibility principle by confining conversion logic to one place.

The costs are an extra layer of indirection and additional classes. Deeply nested or chained adapters can obscure the real call path and add small performance overhead. Adapters also tend to accumulate translation quirks as the two interfaces drift, so they need maintenance as either side evolves.

Related Patterns

Adapter changes an interface; Decorator keeps the same interface but adds behavior; Facade simplifies a whole subsystem behind a new interface; Bridge separates an abstraction from its implementation by design rather than retrofitting. Adapter is closest to Facade but narrower — it targets one class's interface mismatch rather than simplifying many.

Example

An application expects a PaymentProcessor interface with charge(amount). A newly acquired vendor SDK exposes executeTransaction(cents, currency). A VendorPaymentAdapter implements PaymentProcessor wraps the SDK, converting dollars to cents and calling executeTransaction. The rest of the codebase keeps calling charge(amount); only the adapter knows the vendor's shape, so replacing the vendor later means rewriting one class.