Adapter (wrapper)
We use the Adapter design pattern (also known as Wrapper) when working with a component that has an unstable interface or it's interface isn't compatible with our application. It allows wrapping the component with our interface and therefore completely hides the original interface from the application.
Motivation
Most of you, for sure, develop your applications using the component architecture with third-party solutions (or your custom components from previous projects). Imagine that we want (or even need) to use a component which interface is being changed by its authors all the time. We use this component in 100 locations in our program and need to refactor the code in many places every time a new version pops up. A similar problem occurs when we already use some interface in our application and want to connect the component through this interface. It simply doesn't fit.
The Adapter design pattern wraps the component with our interface, so the application is completely shielded from the original component's interface. Whenever this interface changes, we just update our adapter. Thanks to the adapter, we can even change the component to another one easily in case we just don't like it anymore. In that case, we'd only change the adapter again, and the whole program won't even change at all.
Pattern
The pattern is a kind of a mediator between our interface and the interface of a component which is unknown to the application. We can achieve this connection at the object or class level. The pattern therefore has 2 variants.
Object adapter
Client
is a part of our system that calls our interface.
Adaptee
is a component whose interface is unstable, incompatible,
or we just don't want our applications to depend on it. Our interface is defined
in the Adapter
class, which transforms the methods from
Client
to Adaptee
. Adapter
can only serve
as a mediator and just call differently named methods. Or it may contain a
simple logic. As an example, we can imagine calling the
Insert(table, array)
method which gets translated into calling
DatabaseQuery('INSERT INTO table VALUES value1, value2 ...')
. The
array has been transformed into a single parameter and passed to
Adaptee
.
We can see that Client
and Adaptee
are connected
using the Adapter
object.
Possible modification
We can improve the pattern by adding an abstract Target
class.
Adapter
now inherits from the abstract Target
class. The purpose of the abstract class is so we could replace the adapter with
another one easily and be sure that we have maintained compatibility.
Target
can also be just an interface.
Class Adapter
Class Adapter is the less-used variant because it relies on the fact that the
Adaptee
class can be inherited. The resulting adapter is a class
inherited from Target
and Adapter
. Of course,
Target
will be an interface so that multiple inheritance is
possible. We also lose the possibility to work with the Adaptee
children, which we could do with Object Adapter (because the children provide
the same interface as the parent). But the advantage is that we can override
some Adaptee
methods.
Conclusion
Wrappers are often built over databases (we can then change the database without a single change in our application) or over web services that often have complex APIs.