Adapter Pattern

The adapter design pattern is a structural design pattern used to allow two unrelated interfaces to work together. The object that joins these unrelated interfaces is called an Adapter. The definition of Adapter provided in the original Gang of Four book on Design Patterns states: “Convert the interface …

The adapter design pattern is a structural design pattern used to allow two unrelated interfaces to work together. The object that joins these unrelated interfaces is called an Adapter.

The definition of Adapter provided in the original Gang of Four book on Design Patterns states:

“Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.”

1. Understanding the Adapter Pattern

Have you ever tried to use your camera memory card on your laptop? You cannot use it directly simply because the laptop does not have a port that accepts it. You must use a compatible card reader. You put your memory card into the card reader and then inject the card reader into the laptop. This card reader can be called an adapter.

A similar example is your mobile or laptop charger, which can be used with any power supply without fear of the variance in power supplies in different locations. That is also called a power “adapter.”

In programming, the adapter pattern also enables two incompatible interfaces to work smoothly with each other. It lets you wrap an otherwise incompatible object in an adapter to make it compatible with another class.

An adapter pattern is also known as a wrapper pattern. Adapter Design is very useful for system integration when other existing components have to be adopted by the existing system without modifying the source code.

A typical interaction happens like this:

adapter sequence diagram

2. When to use the Adapter Pattern?

The primary use of this pattern is when a class you need to use doesn’t meet the requirements of an interface. e.g. If you want to read the system input through a command prompt in Java then given below code is a common way to do it:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter String");

String s = br.readLine();
System.out.print("Enter input: " + s);

Now, observe the above code.

1) System.in is a static instance of InputStream declared as:

public final static InputStream in = null;

This input stream natively reads the data from the console in a bytes stream.

2) BufferedReader, as Java docs define it, reads a character stream.

//Reads text from a character-input stream, buffering characters so as to 
//provide for the efficient reading of characters, arrays, and lines. 
 
public class BufferedReader extends Reader{..}

Now, here is the problem. System.in provides byte stream where BufferedReader expects character stream. How will they work together?

This is the ideal situation to put an adapter in between two incompatible interfaces. InputStreamReader does exactly this thing and works adapter between System.in and BufferedReader.

/** An InputStreamReader is a bridge from byte streams to character streams: 
  * It reads bytes and decodes them into characters using a specified charset. 
  * The charset that it uses may be specified by name or may be given explicitly, 
  * or the platform's default charset may be accepted. 
  */
 
public class InputStreamReader extends Reader {...}

I hope the above use case makes sense to all of you. Now, the next question is how much work an adapter should do to make two incompatible interfaces work together.

3. How much work the Adapter Pattern should do?

The answer is straightforward: it should do only that much work so that both incompatible interfaces can adapt to each other, and that’s it. For example, in our above case study, an InputStreamReader simply wraps the InputStream and nothing else. Then BufferedReader is capable of using underlying Reader to read the characters in the stream.

/**
 * Creates an InputStreamReader that uses the default charset.
 * @param  in   An InputStream
 */
public InputStreamReader(InputStream in) {
  super(in);
  try {
    sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
  } catch (UnsupportedEncodingException e) {
    // The default encoding should always be available
    throw new Error(e);
  }
}

Also note that if the Target and Adaptee are similar then the adapter has just to delegate the requests from the Target to the Adaptee. If Target and Adaptee are not similar, then the adapter might have to convert the data structures between those and to implement the operations required by the Target but not implemented by the Adaptee.

Now that we have a good understanding of what an adapter looks like, let’s identify the actors used into the adapter design pattern:

4. Design Participants

The classes and/or objects participating in this pattern are listed as below:

  • Target (BufferedReader): It defines the application-specific interface that Client uses directly.
  • Adapter (InputStreamReader): It adapts the interface Adaptee to the Target interface. It’s middle man.
  • Adaptee (System.in): It defines an existing incompatible interface that needs adapting before use in the application.
  • Client: It is your application that works with the Target interface.

5. Adapter Pattern Implementations in JDK

Some other examples worth noticing is as below:

1) java.util.Arrays#asList()

This method accepts multiple strings and returns a list of input strings. It’s very basic usage, but it’s what an adapter does, right?

2) java.io.OutputStreamWriter(OutputStream)

It’s similar to above usecase we discussed in this post:

Writer writer = new OutputStreamWriter(new FileOutputStream("C:\\data\\output.txt"));
writer.write("Hello World!");

3) XmlAdapter#marshal() and #unmarshal()

Adapts a Java type for custom marshaling. Convert a bound type to a value type.

That’s all for this simple and easy topic.

Happy Learning !!

Weekly Newsletter

Stay Up-to-Date with Our Weekly Updates. Right into Your Inbox.

Comments

Subscribe
Notify of
10 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.