Skip to main content

🔌 Adapter

Adapter Pattern that allows objects with incompatible Interfaces to interact with each other.

Example of an adapter.

Example of an adapter. (Image by author, illustrations by Takashi Mifune under free use)

🌍 Real-World Analogy

The ability of using a power plug without adapters: Everyone is happy

The ability of using a power plug without adapters: Everyone is happy (Image by author, illustrations by Takashi Mifune under free use)

Imagine that you are on a long-awaited trip to Japan. Suddenly, you need to charge one of your devices and get a surprise. The power plug and the sockets are different between some countries. That’s why your European plug won’t work in Japan.

The issue can be solved through using a Japanese-style adapter on top of your European-style plug.

🤔 Problem

We are creating a weather forecasting app for our trip. The app is utilizing external sources to display weather data to the user.

We started out with their provided library, which is supplying weather data in XML format.

We can’t directly send weather data to the analytics library as the data format is incompatible.

We can’t directly send weather data to the analytics library as the data format is incompatible. (Image by author, illustrations by Takashi Mifune under free use)

Now, we would like to calculate air quality metrics that aren’t provided by our existing service. The air quality analytics library only works with data in JSON format.

💡 Solution

An adapter is a handy tool that converts one object’s interface to another object’s interface.

It works by wrapping one of the objects to disguise the conversion process taking place behind the scenes. The other object that’s part of the conversion doesn’t even know there’s an adapter involved!

Here’s how it works: Your adapter will have an interface that’s compatible with one of your existing objects. Your existing object can then safely call the adapter’s methods. When the adapter receives a call, it’ll pass the request to the second object, but in a format and order that the second object expects.

Sometimes you can even create a two-way adapter that can convert the calls in both directions! With adapters, you can make even the most incompatible interfaces compatible.

Using an adapter to make XML compatible to JSON

Using an adapter to make XML compatible to JSON (Image by author, illustrations by Takashi Mifune under free use)

To solve incompatible format issues with our weather app, create an XML-to-JSON adapter for the objects used with the Air Quality Analytics Library. Then, adjust the code to communicate solely through the adapter. The adapter will translate incoming XML data into a JSON structure and forward calls to the appropriate analytics object methods.

🏗️ Structure

Structure example

Structure example

  1. Client is where the business logic is stored.
  2. Interface describes properties and methods other classes must follow to collaborate with the Client.
  3. Object Adapter is able to work with both the Client and the Object through a joint Interface.
  4. Object is a third-party or legacy class that is to be used. The Client can’t call the class directly as it is using incompatible interfaces.

How to implement

An typical example of the usage of the Adapter Pattern would be the conversion between different units. In our example, we convert from actual scientific measurements into a human-readable QualityLevel format.

Example implementation

Example implementation

The Adapter pretends to serve AirQualityLevel in a human-readable format, but converts actual scientific values extracted from ExternalWeatherAnalytics.

  1. Identify a 3rd party library that is not compatible with your business logic.
  2. Identify which Interface is required for your business logic and which for your third-party library.
  3. Create an Adapter that implements the Interface required by the business logic.
  4. Implement the conversion logic within the Adapter. Sometimes, mapping can be handy.
  5. Connect everything together.
interface InterfaceWeatherAnalytics {
QualityLevel GetAirQualityLevel(Coordinates coords);
}


class ExternalWeatherAnalytics {
public double GetPM25ByCoord(Coordinates coords) { /.../ }
public double GetVOCByCoord(Coordinates coords) { /.../ }
public double GetPPMByCoord(Coordinates coords) { /.../ }
}


class WeatherAnalyticsAdapter : InterfaceWeatherAnalytics {
private ExternalWeatherAnalytics _target;


public WeatherAnalyticsAdapter(ExternalWeatherAnalytics target) {
_target = target;
}

public QualityLevel GetAirQualityLevel(Coordinates coords) {
double pm25 = _target.GetPM25ByCoord(coords);
double voc = _target.GetVOCByCoord(coords);
double ppm = _target.GetPPMByCoord(coords);

// Calculate QualityLevel based on pm25, voc, and ppm
return QualityLevel;
}

}


// Usage example
class Client {
static void Main(string[] args) {
ExternalWeatherAnalytics externalAPI = new ExternalWeatherAnalytics();
WeatherAnalyticsAdapter adapter = new WeatherAnalyticsAdapter(externalAPI);

Coordinates coords = new Coordinates(latitude, longitude);
QualityLevel airQuality = adapter.GetAirQualityLevel(coords);

// Use airQuality to make decisions in Business Logic
}
}

Pros & Cons

✅ Pros

Single Responsibility Principle. Separate data conversion from business logic.

Open/Closed Principle. Introduce new adapters without changing the underlying code.

❌ Cons

Might overcomplicate things. Sometimes, an easier approach might be suitable.

Might be the wrong approach. Sometimes, adapters might be handy to bridge the gap between poorly designed parts of a software. In such a case, Adapters should not be used.

Relation with other patterns

  • Bridge is developed initially to enable independent development of different parts of an application. On the other hand, the Adapter Pattern is commonly used to integrate incompatible classes with an existing application.
  • Adapter patterns creates a different interface for accessing an existing object, while the Decorator Pattern either maintains or expands the interface.
  • Adapter pattern allows you to access an existing object through a different interface. The Proxy Pattern, however, maintains the same interface while providing additional functionality or control. And the
  • Decorator pattern provides an enhanced interface for accessing the object, typically by adding new functionality to it.
  • Facade Pattern creates a new interface for existing objects, while the Adapter Pattern attempts to make an existing interface usable by converting it to another interface.
  • Bridge, State, Strategy and Adapter patterns share similar compositions, as each pattern delegates work to other objects. Nevertheless, all of these patterns are designed to solve distinct problems.