The adapter pattern is adaptation between classes and objects. Like any adapter in the real world it is used to be an interface, a bridge between two objects.
Contents
- 1 Adapter Pattern’s Intent
- 2 Also Known As
- 3 Adapter Pattern’s Motivation
- 4 Adapter Pattern’s Applicability
- 5 Adapter Pattern’s Structure
- 6 Participants in Adapter Pattern
- 7 Collaborations
- 8 Adapter Pattern’s Consequences
- 9 Adapter Pattern’s Implementation
- 10 Sample Code (Java Example)
- 11 Known Uses
- 12 Related Patterns
- 13 Non-Software Example
Adapter Pattern’s Intent
To convert the interface of one class into another interface that the client expects. Adapter pattern allows two incompatible classes to communicate with one another.
Also Known As
Wrapper
Adapter Pattern’s Motivation
The adapter pattern is adaptation between classes and objects. Like any adapter in the real world it is used to be an interface, a bridge between two objects. In real world we have adapters for power supplies, adapters for camera memory cards, and so on. If you cannot plug in the camera memory card in your laptop, you can use and adapter. You plug the camera memory card in the adapter and the adapter in to laptop slot. That’s it, it’s really simple.
What about software development? It’s the same. Can you imagine a situation when you have some class expecting some type of object and you have an object offering the same features, but exposing a different interface? Of course, you want to use both of them so you don’t want to implement again one of them, and you don’t want to change existing classes, so why not create an adapter.
Adapter Pattern’s Applicability
Use adapter pattern when:
- You want to use an existing class, and its interface is not what you needed.
- You want to create a reusable class that cooperates with the incompatible classes.
- You need to use several subclasses (object adapter only) by adapting to their interfaces (by sub classing each subclass) which is impractical. An object adapter can adapt the interface of their parent class.
Adapter Pattern’s Structure
A class adapter uses multiple inheritance to adapt one interface to another. The structure of class adapter is shown below:
An object adapter relies on object composition. The structure of an object adapter is as shown below:
Participants in Adapter Pattern
Following are the participants in the adapter pattern:
- Target: Defines the domain specific interface the client uses.
- Client: Collaborates with the objects conforming to the Target
- Adaptee: Defines an existing interface that needs to be adapted.
- Adapter: Adapts the interface of the Adaptee to the Traget
Collaborations
Client call operations on an Adapter instance. In turn, Adapter calls Adaptee operations that carry out the request.
Adapter Pattern’s Consequences
Class and object adapters have different trade-offs.
A class adapter:
- Adapts Adaptee to Target by committing to a concrete Adapter As a consequence, a class adapter won’t work when we want to adapt a class and its subclasses.
- Lets Adapter to override some of the behavior of the Adaptee since it is a subclass of Adaptee.
- Introduces only one object, and no additional pointer indirection is needed to get to the Adaptee.
An object adapter:
- Lets a single Adapter work with many Adaptees e the Adaptee itself and all of its subclasses. The Adapter can also add functionality to all Adaptees at once.
- Makes it harder to override Adaptee
Adapter Pattern’s Implementation
Some of the issues to keep in mind while implementing adapter pattern are given below:
- Implementing class adapters in C++: In a C++ implementation of a class adapter, Adapter would inherit publicly from Target and privately from Adaptee. Thus Adapter would be a subtype of Target but not of Adaptee.
- Pluggable adapters: Let’s look at three ways to implement pluggable adapters for the TreeDisplay widget which can lay out and display a hierarchical structure automatically.
The first step, which is common to all three of the implementations discussed here, is to find a “narrow” interface for Adaptee, that is, the smallest subset of operations that lets us do the adaptation. A narrow interface consisting of only a couple of operations is easier to adapt than an interface with dozens of operations. For TreeDisplay, the adaptee is any hierarchical structure. A minimalist interface might include two operations, one that defines how to present a node in the hierarchical structure graphically, and another that retrieves the node’s children.
The narrow interface leads to three implementation approaches:
- Using abstract operations: Define corresponding abstract operations for the narrow Adaptee interface in the TreeDisplay class. Subclasses must implement the abstract operations and adapt the hierarchically structured object.
- Using delegate objects: In this approach, TreeDisplay forwards requests for accessing the hierarchical structure to a delegate object. TreeDisplay can use a different adaptation strategy by substituting a different delegate.
- Parameterized adapters: The usual way to support pluggable adapters in Smalltalk is to parameterize an adapter with one or more blocks. The block construct supports adaptation without subclassing. A block can adapt a request, and the adapter can store a block for each individual request. In our example, this means TreeDisplay stores one block for converting a node into a GraphicNode and another block for accessing a node’s children.
Sample Code (Java Example)
Adapter design pattern is one of the structural design pattern and its used so that two unrelated interfaces can work together. The object that joins these unrelated interface is called an Adapter. As a real life example, we can think of a mobile charger as an adapter because mobile battery needs 3 volts to charge but the normal socket produces either 120V (US) or 240V (India). So the mobile charger works as an adapter between mobile charging socket and the wall socket.
So first of all we will have two classes – Volt (to measure volts) and Socket (producing constant volts of 120V).
//Volt.java
public class Volt {
private int volts;
public Volt(int v){
this.volts=v;
}
public int getVolts() {
return volts;
}
public void setVolts(int volts) {
this.volts = volts;
}
}
//Socket.java
public class Socket {
public Volt getVolt(){
return new Volt(120);
}
}
Now we want to build an adapter that can produce 3 volts, 12 volts and default 120 volts. So first of all we will create an adapter interface with these methods.
//SocketAdapter.java
public interface SocketAdapter {
public Volt get120Volt();
public Volt get12Volt();
public Volt get3Volt();
}
While implementing Adapter pattern, there are two approaches – class adapter and object adapter, however both these approaches produce same result.
- Class Adapter – This form uses java inheritance and extends the source interface, in our case Socket class.
- Object Adapter – This form uses Java Composition and adapter contains the source object.
Class Adapter Implementation
//SocketClassAdapterImpl.java
//Using inheritance for adapter pattern
public class SocketClassAdapterImpl extends Socket implements SocketAdapter{
@Override
public Volt get120Volt() {
return getVolt();
}
@Override
public Volt get12Volt() {
Volt v= getVolt();
return convertVolt(v,10);
}
@Override
public Volt get3Volt() {
Volt v= getVolt();
return convertVolt(v,40);
}
private Volt convertVolt(Volt v, int i) {
return new Volt(v.getVolts()/i);
}
}
Object Adapter Implementation
//SocketObjectAdapterImpl.java
public class SocketObjectAdapterImpl implements SocketAdapter{
//Using Composition for adapter pattern
private Socket sock = new Socket();
@Override
public Volt get120Volt() {
return sock.getVolt();
}
@Override
public Volt get12Volt() {
Volt v= sock.getVolt();
return convertVolt(v,10);
}
@Override
public Volt get3Volt() {
Volt v= sock.getVolt();
return convertVolt(v,40);
}
private Volt convertVolt(Volt v, int i) {
return new Volt(v.getVolts()/i);
}
}
Here is a test program to consume our adapter implementation:
//AdapterPatternTest.java
public class AdapterPatternTest {
public static void main(String[] args) {
testClassAdapter();
testObjectAdapter();
}
private static void testObjectAdapter() {
SocketAdapter sockAdapter = new SocketObjectAdapterImpl();
Volt v3 = getVolt(sockAdapter,3);
Volt v12 = getVolt(sockAdapter,12);
Volt v120 = getVolt(sockAdapter,120);
System.out.println("v3 volts using Object Adapter="+v3.getVolts());
System.out.println("v12 volts using Object Adapter="+v12.getVolts());
System.out.println("v120 volts using Object Adapter="+v120.getVolts());
}
private static void testClassAdapter() {
SocketAdapter sockAdapter = new SocketClassAdapterImpl();
Volt v3 = getVolt(sockAdapter,3);
Volt v12 = getVolt(sockAdapter,12);
Volt v120 = getVolt(sockAdapter,120);
System.out.println("v3 volts using Class Adapter="+v3.getVolts());
System.out.println("v12 volts using Class Adapter="+v12.getVolts());
System.out.println("v120 volts using Class Adapter="+v120.getVolts());
}
private static Volt getVolt(SocketAdapter sockAdapter, int i) {
switch (i){
case 3: return sockAdapter.get3Volt();
case 12: return sockAdapter.get12Volt();
case 120: return sockAdapter.get120Volt();
default: return sockAdapter.get120Volt();
}
}
}
Known Uses
Following are the examples in Java API where adapter pattern is used:
- util.Arrays#asList()
- io.InputStreamReader(InputStream) (returns a Reader)
- io.OutputStreamWriter(OutputStream) (returns a Writer)
- xml.bind.annotation.adapters.XmlAdapter #marshal() and #unmarshal()
Related Patterns
Bridge has a structure similar to an object adapter, but Bridge has a different intent: It is meant to separate an interface from its implementation so that they can be varied easily and independently. An adapter is meant to change the interface of an existing object.
Decorator enhances another object without changing its interface. A 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.
Proxy defines a representative or surrogate for another object and does not change its interface.
Non-Software Example
The Adapter pattern allows otherwise incompatible classes to work together by converting the interface of one class into an interface expected by the clients. Socket wrenches provide an example of the Adapter.
A socket attaches to a ratchet, provided that the size of the drive is the same. Typical drive sizes in the United States are 1/2″ and 1/4″. Obviously a 1/2″ drive ratchet will not fit into a 1/4″ drive socket unless an adapter is used. A 1/2″ to 1/4″ adapter has a 1/2″ female connection to fit on the 1/2″ drive ratchet, and a 1/4″ male connection to fit in the 1/4″ drive socket.
Suryateja Pericherla, at present is a Research Scholar (full-time Ph.D.) in the Dept. of Computer Science & Systems Engineering at Andhra University, Visakhapatnam. Previously worked as an Associate Professor in the Dept. of CSE at Vishnu Institute of Technology, India.
He has 11+ years of teaching experience and is an individual researcher whose research interests are Cloud Computing, Internet of Things, Computer Security, Network Security and Blockchain.
He is a member of professional societies like IEEE, ACM, CSI and ISCA. He published several research papers which are indexed by SCIE, WoS, Scopus, Springer and others.
Leave a Reply