When you have a lot of different ports between your computer and devices, it isn't uncommon to find that you have a few dongles and adapters laying around. If you think about those adapters for a moment, though, they are very interesting pieces of technology; dongles allow us to do things like transfer information flowing into a USB port, translating it in a way that the HDMI port on the other side of the cable can understand.

The goal of adapters? They allow two otherwise unrelated objects to communicate with each other! Interestingly enough, there exists a design pattern that does this exact thing for classes, appropriately called the adapter pattern.

Assumptions

What is the Adapter Pattern?

An adapter is a structural design pattern intended to bridge the gap between two classes that wouldn't cooperate otherwise. This is useful when you don't want to (or aren't able to) edit existing interfaces, but you need a way to compare AppleInterface to OrangeInterface.

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/AdapterPattern_01.png" alt="adapter pattern UML diagram." style="max-width:95%;"> </div>

There are three major components to an adapter:

  1. Target: the class that we want to adapt, or wrap.
  2. Adaptee: the class that we want to make compatible with the target.
  3. Adapter: the class that makes the adaptee speak the language of the target.

In summary, the adapter pattern aims to:

  • Allow classes to work together that have incompatible interfaces.
  • Convert one interface into another interface.

Conceptualization

In the early 2000s, XML was the dominant format for storing and sending structured data. In the 2010s, though, the web industry started to shift towards JSON formatted data, meaning that some people who relied on XML in their application needed to consider their options if 3rd party services began to stop supporting XML in favor of JSON.

Instead of rewriting the entire application around this change in dependencies, if they made an adapter that converted the JSON to XML, then they would at least have a temporary solution!

Let's model some interfaces to get an idea:

interface IXMLparser {
   // Parses XML data into an object.
   Object parse(String data);
   // Converts an object into XML.
   String toXML(Object obj);
 }
// Concrete implementation of our XML parser.
class XMLparser implements IXMLparser {
   Object parse(String data) {
     /*...*/
   }
   String toXML(Object obj) {
     /* Example... */
     return "&lt;ObjectClassAttr&gt;Hello!&lt;/ObjectClassAttr&gt;";
   }
 }

interface IJSONparser {
   // Parses JSON data into an object.
   Object parseJSON(String data);
   // Converts and object into JSON.
   String stringify(Object obj);
 }
// Concrete implementation of our Json parser.
class JSONparser implements IJSONparser{
   Object parseJSON(String data) {
     /*...*/
   }
   String stringify(Object obj) {
     /* Example... */
     return "{'ObjectClassAttr': 'Hello!'}";
   }
 }

XML and JSON were developed by two separate groups, so there wasn't any incentive to derive themselves from a general interface that would've made this all easier. For that reason, we'll create our own adapter to bridge the gap.

When thinking about how to best design our adapter, it can help to imagine how the data will be flowing. In this example, the developer needs to take JSON data from a 3rd party, and then convert it to XML before working on it further:

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/AdapterPattern_02.png" alt="JSON to XML adapter" style="max-width:95%;"> </div>

Now that we're thinking in terms of the data flow, let's look at it in terms of the adapter design pattern:

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/AdapterPattern_03.png" alt="JSON to XML adapter UML" style="max-width:95%;"> </div>

Finally, we can go ahead and model this in pseudocode:

// We are only focusing on adapting the parse() method from XML.
class JSONtoXMLAdapter implements IXMLparser {
   // Reads in JSON data 
   Object parse(String data) {
     IJSONparser json = new JSONparser();
     return json.parseJSON(data);
   }
   
   String toXML(Object obj) {
       // ...
   }
 }

And that's it! Our client code is ready for the change to our adapter:

static void main(String[] args) {
   // Using our third party api that uses JSON now...
   IXMLparser parserAdapter = new JSONtoXMLAdapter();
   // Arbitrary api class.
   BackendAPI api = new BackendAPI();
   // Get the JSON formatted data...
   String jsonFromAPI = api.getImportantData();
   // .. Then parse into an object ..
   Object parsedObject = parserAdapter.parse(jsonFromAPI);
   // .. Finally, translate it to XML..
   String xml = parserAdapter.toXML(parsedObject);
   // .. And send it off to where XML is needed!
   Application.methodThatNeededXMLFormat(xml);
   // Done!
 }