Adapter
Convert the interface of a class into another interface clients expect, letting incompatible classes work together.
A plug adapter for code.
The Adapter pattern is like a travel power adapter: it sits between two things that
don't fit together and translates one interface into another. Your code expects a
MediaPlayer, but you have a third-party Mp4Library with a different API — an
adapter wraps the library and exposes the interface your code wants.
# Problem: our app expects .send(email, body); vendor has .push_mail(...). class AppMailer: def send(self, email, body): ... vendor = VendorSdk() # AppMailer().send(...) can't call vendor.push_mail without edits everywhere.
# Fix: Adapter translates our interface to the vendor's. class MailPort: def send(self, email, body): raise NotImplementedError class VendorMailAdapter(MailPort): def __init__(self, sdk): self.sdk = sdk def send(self, email, body): # Map our names/args onto theirs — only this class knows VendorSdk. self.sdk.push_mail(to=email, text=body) def welcome(mailer: MailPort, user): mailer.send(user.email, "welcome!") welcome(VendorMailAdapter(VendorSdk()), user)
Object vs class adapter.
Participants: the Target (interface the client expects), the Adaptee (existing incompatible class), and the Adapter (implements Target, delegates to Adaptee).
- Object adapter — the adapter holds an instance of the adaptee (composition). Flexible, works in any language.
- Class adapter — the adapter inherits from the adaptee (multiple inheritance). Only where the language supports it.
Boundaries and relations.
- Anti-corruption layer — adapters wrap external/legacy systems so their models don't leak into your domain.
- vs Facade — Adapter changes an interface to match an expectation; Facade simplifies a complex subsystem behind a new one.
- vs Decorator — Decorator keeps the interface and adds behavior; Adapter changes the interface.
- Two-way adapters can implement both interfaces to serve clients of either side.
Interview Questions
A structural pattern that converts the interface of one class into another interface a client expects, allowing classes with incompatible interfaces to collaborate.
A power plug/socket adapter: your device's plug doesn't fit a foreign socket, so the adapter sits between them and makes them compatible without changing either.
Target (the interface the client expects), Adaptee (the existing class with an incompatible interface), and Adapter (implements the Target and translates calls to the Adaptee).
Structural — it's about composing classes/objects to form larger, compatible structures.
When you want to use an existing class (often third-party or legacy) but its interface doesn't match what your code needs, and you can't or don't want to change that class.
Yes — it's sometimes called the Wrapper pattern, because the adapter wraps the adaptee. (Decorator and Proxy are also sometimes called wrappers, so context matters.)
An object adapter uses composition: it holds a reference to an adaptee instance and delegates calls to it. A class adapter uses (multiple) inheritance: it extends both the target and the adaptee. Object adapters are more flexible (can adapt subclasses of the adaptee, work in single-inheritance languages, and can wrap already-created instances), while class adapters can override adaptee behavior and avoid a level of indirection but require multiple inheritance (so they're rare in Java/C#). The object adapter is generally preferred.
Adapter makes an existing interface match a specific expected interface (interface conversion, usually one class), so incompatible parts fit together. Facade provides a new, simplified interface over a whole complex subsystem to make it easier to use — it isn't trying to match a predefined interface, just to reduce complexity. Adapter is about compatibility; Facade is about simplification.
Both wrap another object, but Decorator keeps the same interface and adds behavior/responsibilities, and can be stacked. Adapter changes the interface to a different one the client expects, without adding new functionality (it translates). So: same interface + extra behavior = Decorator; different interface + translation = Adapter.
Java's java.util.Arrays.asList() (adapts an array to a List), InputStreamReader (adapts a byte stream to a character reader), java.io.StringReader, and the old collection Enumeration↔Iterator adapters. In .NET, DataAdapter. Any wrapper that presents an existing thing through a different expected API qualifies.
Adapter is the practical tool for honoring DIP at a boundary. Your high-level code depends on an abstraction (the Target interface it owns); the adapter implements that abstraction by delegating to a concrete external class (the adaptee). So the dependency points from the detail (adapter/adaptee) toward your abstraction, not from your code toward the third party. This is exactly how "ports and adapters"/hexagonal architecture keeps the domain independent of infrastructure.
Yes. An adapter can compose and delegate to more than one underlying object to fulfill the target interface, aggregating their capabilities. This starts to blur toward a Facade if it's coordinating a whole subsystem, but it's legitimate for an adapter to translate one target API onto calls across several adaptees when that's what the mapping requires.
In Domain-Driven Design, an anti-corruption layer (ACL) prevents another system's model/terminology from leaking into and corrupting your domain. Adapters are the primary building block: they translate between the external system's interface and data model and your domain's interfaces and objects, so your core only ever sees its own clean abstractions. When the external system changes, only the adapter/ACL changes; the domain is insulated. This scales the "adapt an incompatible interface" idea from a single class to an entire integration boundary, combining adapters with translators/facades to protect model integrity and localize the impact of external change — crucial for legacy integration and third-party services whose models you don't control.
An adapter can only bridge what the adaptee can actually do; if the target interface promises semantics the adaptee can't fulfill, the adapter must fake, partially implement, or throw — risking LSP violations where clients relying on the target contract break. Impedance mismatches (different error models, threading/async assumptions, data granularity, or performance characteristics) may force lossy or leaky translation. Adapters can also proliferate and add indirection/latency, and a "fat" adaptee forced into a narrow target may hide needed capabilities (or vice versa). Mitigations: keep target interfaces narrow (ISP) so adapters aren't forced to implement unsupported methods, verify adapters against the target's contract tests to ensure substitutability, and accept that some external capabilities simply won't map — in which case a different abstraction, not an adapter, is the honest answer.
A two-way adapter implements both interfaces (the target and the adaptee's) so it can be used transparently by clients coded to either side. It's useful when two subsystems must interoperate and each expects objects of its own interface — for instance, a class that is both a legacy Shape and a new Drawable, so old code sees a Shape and new code sees a Drawable, without duplicating the object. Implementation-wise it typically holds/extends the adaptee and maps calls in both directions, ensuring state stays consistent across the two views. The risk is increased complexity and the danger of the two interface contracts having conflicting semantics; it works best when the two interfaces are largely complementary views of the same underlying object rather than contradictory abstractions.
All are "wrappers," distinguished by intent and interface relationship. Adapter: wraps to change the interface to a different expected one (compatibility), same behavior. Decorator: wraps to keep the same interface and add responsibilities, and is designed to stack recursively. Proxy: wraps to keep the same interface and control access (lazy loading, remoting, security, caching) — same interface, same conceptual operation, added access control rather than new features. Facade: wraps a whole subsystem behind a new, simplified higher-level interface (it defines its own, not matching a predefined one), and doesn't need to preserve any existing interface. So ask: Am I matching a required interface (Adapter), adding features transparently (Decorator), governing access to one object (Proxy), or simplifying many objects (Facade)? The structural code can look similar; the distinguishing factor is purpose and how the wrapper's interface relates to the wrapped object's.