Description
Adapter Design pattern is listed underneath the Structural patterns. As you understand from the name it is designated to act like an adapter. When you look for this pattern there are different descriptions and examples, however I find the below illustration very clear what the pattern stands for;
As you study the illustration we see three elements here;
- Client: Expects to work with the Adaptee, however it is not compatible with the client,
- Adapter: Wraps the Adaptee’s logic and provides a way to utilize adaptee,
- Adaptee: An interface or an abstract class that has legacy methods which are not compatible with the client.
The stand point of the pattern is to work the clients with the incompatible interfaces by not chancing or enhancing the methods of the adaptee via the Adapter classes/interfaces. On the other hand also Adapters known as a Wrapper classes. Furthermore, the adapter pattern also practices the Open Close principle in the S.O.L.I.D. in a way that we will not change the existing code unit that will be closed, however we will wrap it by using an adapter. In the real world examples you will see US/Europe electricity socket voltage adapters and all in one memory card, card reader a computer and so on.
Last of all the Adapter Pattern may have some similarities to;
- Façade Pattern: It provides a simple interface and methods for complex structure,
- Decorator Pattern: Enhances an interface and adds more functionalities
However, the Adapter pattern does neither of those, but provides a way to the client to work with an incompatible interface by either encapsulating the logic or delegating it. Observe the following to apprehend the topic.
Forming the Pattern
The adapter pattern can be form in two different ways;
Class Adapter
The adapter class utilizes Inheritance. The Adapter implement/extends the Adaptee interface/abstract class and overrides the methods,
Object Adapter
The adapter class utilizes Composition. The adapter has an instance of the Adaptee interface/abstract class and delegates the request to it.
Example
For the sake of the simplicity, I’ll emphasis on an old main frame that needs an adapter for classes that work on the Strings with tabs after each enter.
The objects and the roles we have:
- MainFrameSender: it is the main interface that has we will wrap, it only accepts a String as a parameter
- MainFrameSenderImpl: it is the implementation of the Main Frame interface,
- ClassMainFrameSenderAdapter: The adapter class that is formed with the Class Adapter format.The Adapter will convert the Content object into the String that the interface accepts,
- ObjectMainFrameSenderAdapter: The adapter class that is formed with the ObjectAdapter format. The Adapter will convert the Content object into the String that the interface accepts,
- Content: This is the Pojo class that will be used to carry the data by the clients,
- Main: It is used as a client and expose the capabilities of the each adapter implementation.
MainFrameSender.java
public interface MainFrameSender { void sendToMainFrame(String content); }
MainFrameSenderImpl.java
public class MainFrameSenderImpl implements MainFrameSender { @Override public void sendToMainFrame(String content) { System.out.println("sending the content to the mainframe: " + content); } }
ClassMainFrameSenderAdapter.java
public class ClassMainFrameSenderAdapter implements MainFrameSender { @Override public void sendToMainFrame(String content) { System.out.println("sending the content to the mainframe: " + content); } public void sendToMainFrame(Content content) { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(content.getMessageId()); stringBuilder.append("\t"); stringBuilder.append(content.getHeader()); stringBuilder.append("\t"); stringBuilder.append(content.getContent()); sendToMainFrame(stringBuilder.toString()); } }
ObjectMainFrameSenderAdapter.java
public class ObjectMainFrameSenderAdapter { private MainFrameSender mainFrameSender = new MainFrameSenderImpl(); public void sendToMainFrame(Content content) { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(content.getMessageId()); stringBuilder.append("\t"); stringBuilder.append(content.getHeader()); stringBuilder.append("\t"); stringBuilder.append(content.getContent()); mainFrameSender.sendToMainFrame(stringBuilder.toString()); } }
Content.java
public final class Content { private int messageId; private String header; private String content; public Content(int messageId, String header, String content){ this.messageId=messageId; this.header=header; this.content=content; } public int getMessageId(){ return messageId; } public String getHeader(){ return header; } public String getContent(){ return content; } @Override public String toString() { return "Content{" + "messageId=" + messageId + ", header='" + header + '\'' + ", content='" + content + '\'' + '}'; } }
Main.java
public class Main { public static void main(String[] args) { Content content = new Content((int)Math.random(), "Message 1","this message sent via Class Adapter"); MainFrameSender classMainFrameSenderAdapter = new ClassMainFrameSenderAdapter(); ((ClassMainFrameSenderAdapter) classMainFrameSenderAdapter).sendToMainFrame(content); Content content2 = new Content((int)Math.random(), "Message 2","this message sent via Object Adapter"); ObjectMainFrameSenderAdapter objectMainFrameSenderAdapter = new ObjectMainFrameSenderAdapter(); objectMainFrameSenderAdapter.sendToMainFrame(content2); } }
Console Output
sending the content to the mainframe: 0 Message 1 this message sent via Class Adapter sending the content to the mainframe: 0 Message 2 this message sent via Object Adapter