Adapter
Let two objects with incompatible interfaces collaborate by putting a translator between them.
The problem
You are building a stock market dashboard. Your app downloads XML feeds and draws handsome charts. Then you find a third-party analytics library that would make the product genuinely smarter, and it turns out to speak only JSON.
Two bad options present themselves. Rewrite the library to accept XML - assuming you even have the source, and assuming you enjoy breaking everyone else who depends on it. Or scatter conversion code through your own classes at every call site, which works right up until the second incompatible library arrives.
The same standoff in miniature: a round hole and a square peg.
The solution
Add a third object whose entire job is translation. The adapter implements the interface the client already calls, holds a reference to the awkward service, and converts on the way through: format, units, argument order, whatever the mismatch happens to be. The wrapped object never learns it has been wrapped.
Three moves, in order: the adapter takes on an interface the existing code can call; the existing code calls the adapter as if nothing unusual is happening; the adapter forwards the request to the service in the shape the service wants. Ambitious versions translate both directions.
- 1The client wraps the foreign object once, at the boundary. Everything downstream stays innocent.
- 2The hole is handed something that type-checks as a RoundPeg. It has no idea a square is hiding inside.
- 3The hole calls the only method it knows, exactly as it would on a real round peg.
- 4The adapter turns around and speaks the service language: widths, not radii.
- 5The translation happens here and nowhere else - the radius of the smallest circle the square would fit into.
- 6A perfectly ordinary radius comes back. The conversion cost the client zero lines.
- 7The hole compares numbers and answers. Both sides remain blissfully unaware of each other.
Structure
The object adapter above uses composition - it wraps the service - and works in every language. There is also a class adapter that inherits from both sides at once and overrides the mismatched methods, which requires multiple inheritance and is therefore mostly a C++ story.
Code
Square pegs, round holes, and the small liar that reconciles them.
When to use it
- You want to use an existing class whose interface does not match the rest of your code, and changing that class is impossible or unwise - the classic legacy or third-party case.
- You want several existing subclasses to gain a shared capability that cannot go into their superclass. Rather than duplicating it into every child, put it in an adapter and wrap the objects that need it.
Pitfalls
- Layer for layer's sake. Every adapter is one more interface and one more class. If you control the service, editing it is often the smaller change.
- The adapter that grows a brain. Translation only. The moment business rules move in, you have built a second implementation nobody knows about.
- Adapters all the way down. They do not compose the way decorators do. A chain of adapters is usually a sign that two subsystems need a real boundary, not more tape.
Don't confuse it with
- Decorator. A decorator keeps the interface intact and adds behavior behind it, and because its input and output types match, decorators stack. An adapter changes the interface on purpose, which is exactly why it does not stack.
- Proxy. A proxy also presents the same interface as its service, but its motive is control: lazy loading, caching, permissions. An adapter has no opinion about access, only about vocabulary.
- Facade. A facade builds a new, deliberately simpler interface in front of an entire subsystem. An adapter usually wraps a single object and is trying to make an existing interface usable rather than pleasant.
- Bridge. Bridge is drawn on the whiteboard before either hierarchy exists so both can evolve freely. Adapter shows up later, with a screwdriver, once two things that already exist refuse to connect.
Check yourself
Which interface does an object adapter implement?