Avijeet Dash & Satyabrata Dash
Intent
Convert the interface of a class into another interface that clients expect.
In 30 Seconds
Here the catch is "achieving reusability by converting an existing interface to a new interface". Let’s say we have a class ‘’A’’ that has getX() method signatures. But the client code uses get ("X") method signature for calculation. Now how to bridge this interface (function signature) difference?
We need can use an one Adapter class here to wrap the which will convert all
the getX() methods to inside get("X") methods. We can write an adapter
(wrapper) class ‘B’ around ‘A’ to make it act like a hash table (this is
the target interface which will give us get ("X") type of signature).
Adapter (B) adapts the Adaptee (A) interface to the Target’s ( HashTable)
interface. Thus avoids rewriting all the Adaptee (A) code just for the sake of
the new interface.
Motivation
Two styles of implementation of the pattern are possible. We can implement
the pattern in two ways: class adapters and object adapters. Class
adapters are realized by subclassing both target interface and the adaptee
implementation. This is to yield a single object (multiple inheritance). Object
adapters are realized by delegating operations from the adapter object to the
adaptee object by having adaptee object as attributes (object composition).
A class adapter uses inheritance so it can be used to adapt only a class and
all its parents but not subclasses. A Class adapter introduces only one object
and hence, avoids additional pointer indirection (which happens in languages
like c++ where pointers are used for object references). Object adapters on the
other hand use composition to make an Adapter work with many Adaptees. Here
Adapter can’t override Adaptee behavior as it is doesn’t know which Adaptee
it is working with.
Structure
Class Adapter and Object Adapter
Click here https://www.ciol.com/images/adapter.gif
to view the image
Sample Code
/*
EXAMPLE-1
In a Class Adapter pattern, Adapter extends the Adaptee. When the
adapter is used in client code, it works as a Hashtable and gives get
("X") function signature.
The class adapter is supposed to extend the Target as well, but Java doesn’t
support multiple inheritance, which is achieved by using multiple interface
implementation. In this case, we can implement Map interface that Hashtable
implements. But for simplicity we have ignored the implementation of the
interface.
*/
class myAdapter extends Adaptee
{
Hashtable variables = new Hashtable( 20);
public myAdapter()
{
// calling methods of parent class i.e. the Adaptee
variables.put( "AUTH_TYPE" , getAuthType());
variables.put( "REMOTE_USER", getRemoteUser());
//other methods
}
public Object get(Object key)
{
return variables.get( key );
}
//other methods
}
/*
This is an object adapter, where myAdapter is the Adapter, ‘Adaptee’ is
the adaptee and Hashtable is the Target. The Adapter delegates the
responsibilities to an adaptee to get the values.
*/
class myAdapter extends Hashtable
{
Hashtable variables = new Hashtable(20);
// composition of an object
public myAdapter( Adaptee adaptee )
{
variables.put( "AUTH_TYPE" , adaptee.getAuthType());
variables.put( "REMOTE_USER" , adaptee.getRemoteUser());
//other methods
}
public Object get(Object key)
{
return variables.get( key );
}
//other methods
}
/*
EXAMPLE — 2
Here we have adapted one Local class here to make that remotely
available. The local class neither extends UnicastRemoteObject nor its
operations throw RemoteException as required by java RMI specification. So we
have defined an Adapter class that wraps around the Local class and also
conforms to RMI specification to be remotely available.
Java doesn’t support multiple inheritance. But the Remote class must extend
UnicastRemoteObject class. So it is not possible to extend the Adaptee class in
our example. So we have adapted Object Adapter here. But this design conforms to
the basic Design principle of favoring Composition to Inheritance.
*/
//Adaptee Class which is a local class
class CustomerInfo{
private int custId;
private float salary;
public float getSalary(int custId){
/*
Write code here to fetch salary from database
it is hardcoded here to Rs. 1000 for simplicity
*/
salary=1000;
return salary;
}
}
// Declare the Remote Interface
interface IRemoteCustomerInfo extends java.rmi.Remote{
/*
All remote methods throw java.rmi.RemoteException as part of
java RMI requirement.
*/
public float getSalary(int custId) throws java.rmi.RemoteException;
}
// Adapter class that adapts a local class to a remote implementation
public class RemoteCustomerInfoAdapter extends
java.rmi.server.UnicastRemoteObject
implements IRemoteCustomerInfo{
protected CustomerInfo adaptee;
public RemoteCustomerInfoAdapter(CustomerInfo customerInfo)
throws java.rmi.RemoteException{
this.adaptee=adaptee;
}
public float getSalary(int custId) throws java.rmi.RemoteException{
return adaptee.getSalary(custId);
}
}
RemoteCustomerInfoAdapter is now ready for use as a Remote class.
Complexities simplified
Pluggable Adapters
In Example-1 we adapted a class with getX() methods to a
Hashtable interface. It is likely that we may adapt another class with getX()
methods to a Hashtable in the future. It would be appropriate to write one class
to do all such adapting. Pluggable adapter Adapter adapts dynamically to one of
the adapters. Adapter decides which class it adapts based on parameters or
differing constructors.
Two-way Adapters
Two-way Adapters are more transparent to the clients than one-way
Adapters as they conform to the interfaces of both Adaptee and Target. So the
client can use both the interfaces with the same Adapter class without knowing
anything about the Adaptee. We can do that in Example-1 by providing both getX()
and get("X") interfaces.
Also Known as and Related Patterns
This pattern is also known as Wrapper.
Bridge has similar structure as an object adapter, but it has a different
intent of separating the interface from the implementation.
Decorator enhances an object’s responsibility dynamically without
changing the interface and hence more transparent than Adapters.
Proxy defines a representation for another object without changing its
interface.