Adapter Design Pattern in Java

Ever tried to use a your camera memory card in your laptop. You cannot use it directly simply because there is no port in laptop which accept 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 the adapter.

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

In programming as well, adapter pattern is used for similar purposes. It enables two incompatible interfaces to work smoothly with each other. Going by definition:

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 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.”

An adapter pattern is also known as Wrapper pattern as well. Adapter Design is very useful for the system integration when some other existing components have to be adopted by the existing system without sourcecode modifications.

A typical interaction happen like this:

adapter sequence diagram

Where to use Adapter Design Pattern?

The main use of this pattern is when a class that you need to use doesn’t meet the requirements of an interface. e.g. If you want to read the system input through command prompt in java then given below code is 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 carefully.

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

	public final static InputStream in = null;

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

2) BufferedReader as java docs define, 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 they will work together?

This is the ideal situation to put a 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 usecase makes sense to all of you. Now, the next question is how much work adapter should do to make two incompatible interfaces work together?

How much work the Adapter Pattern should do?

Answer is really simple, it should do only that much work so that both incompatible interfaces can adapt each other and that’s it. e.g. in our above case study, A InputStreamReader simply wraps the InputStream and nothing else. Then BufferedReader is capable of using underlying Reader to read the characters in 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 when we have a good understanding of what’s an adapter looks like, let’s identify the actors used into adapter design pattern:

Participants of Adapter Design Pattern

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 using in application.
  • Client: It is your application that works with Target interface.

Other example implementations of Adapter Design Pattern

Some other examples worth noticing is as below:

1) java.util.Arrays#asList()

This method accepts multiple strings and return a list of input strings. Though 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) javax.xml.bind.annotation.adapters.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 !!

Comments

Subscribe
Notify of
guest
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.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode