The Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
The Adapter Pattern is used when an existing adaptee class is needed and its interface is not the one a client needs. For this, the pattern changes an intefcace into one a client expects using a adapter class. To achieve this, there are two forms of the Adapter Pattern: object and class adapters. Class adapters require multiple inheritance. They inherit from the adaptee and the target interface, which is expected by the client. With the object adapter, the adapter implements the target interface and wraps the adaptee by holding an instance of it.
The client is implemented against the target interface and uses the adapter in the following way:
- The client makes a request to the adapter by calling a method on it using the target interface.
- The adapter translates the request into one or more calls on the adaptee using the adaptee interface
- The client receives the results of the call and never knows there is an adapter doing the translation.
Another way to implement an adapter in programming languages that support multiple inheritance like C++ is to let the adapter class inherit both the target interface and the adaptee. This is known as Class Adapter. An Object Adapter uses composition to pass requests to an Adaptee.
Adapters are similar to the Facade Pattern and the Decorator Pattern. An adapter wraps an object to change its interface, a decorator wraps an object to add new behaviors and responsibilities, and a facade “wraps” a set of objects to simplify.
The following example uses modified classes found in the example of the Strategy Pattern. Here we have the following classes. A Duck interface acting as the target interface that a client expects to see:
An implementation of this target interface is the MallardDuck
:
The object that we are going to adapt, the adaptee, is the following Turkey
class:
A concrete implementation of this interface is WildTurkey
:
The followign main program uses adapters to adapt a Turkey
to a Duck
:
The adapter that is used here converts a Turkey
to a Duck
. Therefore it needs to implement the target interface Duck
.
It has a reference to its adaptee, the Turkey
, which is initialized through the constructor. The class implements the methods of its target interface using the adaptee reference:
The output of the program is:
Another real-life example is to use an adapter between Java iterators and enumerators.
In Java, early collection types (Vector, Stack, Hashtable, and a few others) implement a method, elements()
,
which returns an Enumeration
. The Enumeration
interface allows you to step through the elements of a collection
without knowing the specifics of how they are managed in the collection.
In later versions of Java this was replaced with Iterators
which has also a remove()
method:
To deal with legacy code, that exposes the Enumeration
interface, yet we’d like for our new code to use only Iterator
s.
An adapter to deal with this situation would need to implement the Iterator
as its target interface and be composed with
Enumeration
as its adaptee:
The hasNext()
and next()
methods are straightforward to map from target to adaptee: we just pass them right through. The best we can do for remove()
is to throw a runtime exception because Enumeration
does not support removing.
The following code test the above EnumerationIterator
adapter:
Reference
Buy
The code examples for the single patterns are from the book Head First Design Patterns and can be found on their GitHub page.
Comments