Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
Imagine that you have an app that works with data in XML format, but at some point, you need to use a library that can only work with JSON.
For example, your app works with a stock data. It downloads data from multiple data providers in XML and builds nicely looking charts. In a new version, you decided to improve the app and use some smart analytics library. But there is a catch: the analytics library only works with data in JSON format.
In cases like this, you could either rewrite your code to support JSON or change the library itself to work with XML. The first choice may break your existing code, and the second might simply be impossible if the library's code is unavailable.
You can create an Adapter. It is a special object that converts calls sent by one object to the format that another object can understand. Adapter wraps one of the objects to hide the complexity of conversion happening behind the scenes.
Adapters can convert not only data formats but also interfaces. For instance, adapter receives a call to method A and then passes it to methods B, C and D of a wrapped object.
Sometimes it is even possible to create two-way adapters that convert calls in both directions.
So, the stock market app we discussed above will get a special
XML_To_JSON_Adapter class. It convert all incoming data from XML to JSON before passing it to methods of a wrapped object of the analytics library. This way you will not need to change any existing code: neither app's classes, nor the analytics library.
Power plugs and sockets in different countries
When you fly from the US to Europe for the first time, you may get a surprise when you try to charge your laptop. The power plug & sockets standards vary between countries. That is why your US plug will not fit the German socket.
The problem can be solved by a power plug adapter that has the American-style socket and the European-style plug.
This implementation uses composition principle: adapter implements the interface of one object and wraps the other one. It can be implemented in all major programming languages.
This implementation uses inheritance. Adapter inherits both interfaces at the same time. It can be implemented only in languages that support multiple inheritance, such as C++.
Existing interface or class that is already supported by the rest of your code.
Service is some useful class (usually 3rd-party or legacy) that can not work directly with application's classes.
Adapter implements Existing interface and holds a reference to an object of the Service class.
The adapter receives calls from client code via the methods of Existing interface. It may correct type or format of the data, passed through the parameters, before passing it along to methods of the Service object.
Client uses Adapter object through a Existing interface.
This allows adding new Adapter classes to the program without changing existing code (this situation may arise when the Service class changes, for example, when you update the 3rd party library).
The class adapter does not need to wrap any objects. It implements both interfaces at the same time. Thus it is compatible with both objects.
Let's see how Adapter can convert one interface into another with some basic data conversion. The example is based on the conflict between round holes and square pegs. Round holes work great with round pegs; you can determine whether or not one fits another just by comparing their radiuses. But square pegs do not have the radius measurement at all.
That is why we create an Adapter class that wraps square peg objects and pretends that it has a radius, which equals to half of the square's diameter (in other words, minimal radius of the circle that can contain that square).
When you want to reuse existing class, but its interface is not compatible with the rest of application's code.
The Adapter pattern creates a middle layer class that translates application calls to the form that the existing class understands.
You need to reuse several existing classes, but they lack some common functionality. And you can not add it to the superclass because it is either closed or used in other code.
You could put the missing functionality into a new adapter class. It will connect your app's code and the classes you are interested in. This solution looks very similar to the Visitor pattern.
How to Implement
Make sure that you have two actors:
- Useful service objects.
- Application code that has to use service objects. The application should not be able to use the service objects directly, because of incompatible interfaces or data formats.
Declare the client interface that the future adapter class will be following. The application will use this interface to communicate with an adapter.
Create an adapter class, make it implementing the client interface, but leave all methods empty for now.
Add a field into the adapter class that will store a reference to a service object. In most cases, this field gets the value in a constructor. In simpler cases, the adaptee can be passed directly to the adapter methods.
One by one, implement all methods of the client interface in adapter class. These methods should pass execution to appropriate methods of the service object, while converting any passed data to a proper format.
Once the adapter class is ready, use it in application code via the client interface.
Pros and Cons
- Hides from the client code unnecessary implementation details of interface & data conversion.
- Increases overall code complexity by creating additional classes.
Relations with Other Patterns
Bridge is designed up-front to let the abstraction and the implementation vary independently. Adapter is retrofitted to make unrelated classes work together. Adapter makes things work after they're designed; Bridge makes them work before they are.
Adapter is meant to change the interface of an existing object. Decorator enhances another object without changing its interface. Decorator is thus more transparent to the application than an adapter is. As a consequence, Decorator supports recursive composition, which isn't possible with pure Adapters.
State, Strategy, Bridge (and to some degree Adapter) have similar solution structures. They all share elements of the "handle/body" idiom. They differ in intent - that is, they solve different problems.