Saturday, August 3, 2019

Adapter Pattern

Adapter pattern is a structural pattern. It enables us to make classes work together which otherwise can't because of incompatible interfaces.

Suppose we have an existing software, and we are making use of a third party library to get some work done. Library has an interface exposed to work with it, and hence we have designed our software to be compatible with this interface. But down the line, there is a new library available and it performs better than our current library. So we decide to replace older one with this new super library.  But there is hurdle. New library exposes an interface which is different from interface we have designed our software to. We don't want to change our existing design, and of-course we can't change new library's interface.
Adapter pattern comes to our rescue. It enables us to design a class which sits between our software and new library and acts as a mediator and adapts the new library interface into what our software expects. This mediator class is called Adapter. It basically receives requests from a client and transform them into requests which a receiver can understand.

Code structure

Adapter pattern enables us to encapsulate the changes which we need to do when ever interface we need to work with undergoes a change. This decouples client code from the interface changes, and its the adapter class which undergoes that change and hence client doesn't need to be modified whenever client needs to work against a different interface.


Client only knows of interface it is designed to work with. We call it Target. Adapter class implements Target. It is composed of Adaptee which has the interface Client is not compatible with. but it has the action that Client wants to perform. So Client invokes request on Adapter which delegates the request to Adaptee.
If Adaptee changes, changes we need to make are only in Adapter and hence Client is decoupled from Adaptee.

Adapter pattern is heavy with good OO design principles. Client is bind to an interface, rather than an implementation. Also, Adapter uses composition to wrap the Adaptee. Moreover, we can now use Adapter with subclasses of Adaptee.

Object and class adapter

In Adapter pattern discussion so far we have wrapped Adaptee object inside an Adapter. This is object based approach and it uses composition. We can also have a class based approach wherein Adapter subclasses an Adaptee class. So in class based approach, we use inheritance instead of composition. This reduces flexibility as object based approach enables us to work with subclasses of Adaptee. But in class based approach we can override Adaptee's behaviour.

Example

Older types of Java collections like Vector and Stack provides Enumeration interface to work with these collections. Enumeration has methods to step through the collection, like hasMorElements, nextElement().Later Java versions introduced Iterator interface to iterate step through the new types of Collections, like List. 

If we want to make use of Iterator interface in our code, but are still working with legacy code which exposes Enumeration interface, we can make use of Adapter pattern.

Our code becomes Client, Iterator becomes Target interface, Enumeration class becomes Adaptee. We need to design Adapter class to make our code work with Iterator while underneath Adapter interacts with Enumeration. Two methods in Enumeration directly maps to Iterator methods(hasNext(), next()), but Enumeration doesn't have any method which corresponds to remove() in Iterator. So we make a compromise here, and throw UnsupportedOperationException in our implementation of remove() in Adapter class.



What we may not like

As requests are delegated, so there is an overhead.

No comments:

Post a Comment