In programming, a common problem is an interface mismatch. A class exists, but its interface is not what the system expects. The Adapter design pattern lets classes with incompatible interfaces work together.
There is no need to rewrite code or change existing classes. The Adapter “translates” one interface into another. It does this without modifying existing code.
Let’s consider an application that shows a weather forecast: WeatherClient. The application needs an object that implements the WeatherService interface. It calls the method getCurrentTemperature() on that object.
public class WeatherClient {
WeatherService weatherService;
public WeatherClient(WeatherService weatherService) {
this.weatherService = weatherService;
}
public void showTemperature (String city) {
System.out.println(city + ": " + weatherService.getCurrentTemperature(city));
}
}
The WeatherService interface declares 1method, getCurrentTemperature(). It returns a String:
public interface WeatherService {
String getCurrentTemperature(String city);
}
But the weather data comes from an external API. The ExternalWeatherApi interface declares one method, getTemperatureCelsius(). It returns a double:
public interface ExternalWeatherApi {
public double getTemperatureCelsius (String city);
}
To connect with API, the code needs an object that implements ExternalWeatherApi. But this object can’t be passed to WeatherClient, because WeatherClient expects object implementing WeatherService:
public class Main {
public static void main (String[] args) {
ExternalWeatherApi weatherApi = new ExternalWeatherApi() {
@Override
public double getTemperatureCelsius(String city) {
return 15.2;
}
};
// WeatherClient weatherClient = new WeatherClient(weatherApi); // ERROR
}
}
Passing weatherApi to WeatherClient causes an error. The Adapter solves this problem.
Implementacja of the Adapter pattern
The Adapter translates an object which implements ExternalWeatherApi interface to WeatherService interface.
The class ExternalApiToWeatherServiceAdapter implements WeatherService interface (required by WeatherClient). It also has an ExternalWeatherApi object (needed to call the external API) as a field.
public class ExternalApiToWeatherServiceAdapter implements WeatherService{
ExternalWeatherApi weatherApi;
public ExternalApiToWeatherServiceAdapter(ExternalWeatherApi weatherApi) {
this.weatherApi = weatherApi;
}
@Override
public String getCurrentTemperature(String city) {
return weatherApi.getTemperatureCelsius(city) + " st. C";
}
}
Thanks to this, an object of ExternalApiToWeatherServiceAdapter class can be passed to WeatherClient and WeatherClient can call getCurrentTemperature() defined by WeatherService:
public class Main {
public static void main (String[] args) {
ExternalWeatherApi weatherApi = new ExternalWeatherApi() {
@Override
public double getTemperatureCelsius(String city) {
return 15.2;
}
};
ExternalApiToWeatherServiceAdapter apiAdapter =
new ExternalApiToWeatherServiceAdapter(weatherApi);
WeatherClient weatherClient = new WeatherClient(apiAdapter);
weatherClient.showTemperature("Łódź");
}
}
Program output in the console is: Łódź: 15.2 st. C
Summary
The Adapter pattern is a clean solution to incompatible interfaces. It enables reuse of existing code without changing it. It helps integrate different components while keeping the architecture consistent and flexible.
Adapter is common in integrations with external APIs, libraries, and legacy systems. It is especially useful when integrating old systems to new components. In production, an adapter rarely calls just one method. It often transforms whole objects to move them from one interface to another.
There is also a two-way adapter too. It adapts interfaces in both directions with one object. Such an adapter must implement both interfaces. A two-way adapter is less clear and less common.
The Adapter pattern is similar to the Facade pattern, but they have different goals:
- the goal of Adapter is to modify an interface so it fits the client’s needs,
- the goal of Facade is to provide a simple interface to a system or subsystem.
