Adapter Design Pattern
Adapter design pattern is one of the structural design pattern. Adapter Pattern is used when we have to make a relationship between two incompatible interfaces so that they can work together. The object that joins these unrelated interface is called an Adapter. As a real life example, we can think of a mobile charger as an adapter because mobile battery needs 3 volts to charge but the normal socket produces either 120V (US) or 240V (India). So the mobile charger works as an adapter between mobile charging socket and the wall socket.
Adapter Design Pattern is used when we have to make a relationship between two incompatible interfaces so that they can work together. This pattern is particularly useful when you want to integrate classes that couldn’t otherwise work together due to differing interfaces.
Assume that you are creating an application for weather forecasting. Your application collects weather information from several sensors in a proprietary binary format, which you display on a user-friendly interface. The following figure shows different components of this application.
You choose to incorporate an advanced, third-party weather forecast system into your app to increase its accuracy. However, there is a twist: this method only accepts data in ordinary XML format, not the binary format provided by your sensors. The discrepancy in the data formats presents a problem.
Can you think of some solution to this problem? The solution is to create an Adapter. Here we go!
The above picture illustrates the connection between the weather forecast system, which needs XML format, and the weather data sensor, which generates data in a proprietary binary format. For the sensor and the prediction algorithm to work together seamlessly and be compatible, the Adapter is essential in transforming the binary input into XML. The above illustration makes it easier to see how the Adapter Design Pattern can be used to address the problem of incompatible data formats.
Real-world Example
To better understand the concept of the Adapter design pattern, can you think of some real-world examples? Let’s look at some example.
Here is a simple pictorial illustration showing a real-world example of the Adapter design Pattern. The picture shows a view of a diplomatic meeting in which two diplomats, from different countries, speaking different languages, are communicating with each other with the help of a translator. Here, the translator translates the speech from both diplomats and acts as an adapter between two incompatible persons. This enables effective communication between the two parties despite their language barrier, just like the Adapter Pattern allows software components with incompatible interfaces to work together.
Structure of Adapter Design Pattern
The structure of the Adapter design pattern can be shown using a class diagram. The components of class diagram are:
- Client: The class that contains the actual business logic. It interacts with the
Adapter
to use the service. - Target Interface: This interface defines how the client wants to use the service. The
Adapter
class implements this interface. - Adapter: This class implements the
Target
interface. It also contains a reference to theAdaptee
class. It translates the interface from theTarget
to something theAdaptee
understands. - Adaptee: This is the class that implements the actual service that the
Client
wants to use but can’t use directly because it has an incompatible interface with theClient
.
Implementation of Adapter Pattern
Pseudocode
Let’s implement the previously mentioned translator example and have a look at its pseudocode.
// Adaptee
CLASS FrenchSpeaker
METHOD speakFrench(message)
PRINT "Speaking in French: " + message
END METHOD
END CLASS
// Target Interface
INTERFACE EnglishSpeaker
METHOD speakEnglish(message)
END INTERFACE
// Adapter
CLASS Translator IMPLEMENTS EnglishSpeaker
PRIVATE frenchSpeaker: FrenchSpeaker
CONSTRUCTOR Translator(frenchSpeaker: FrenchSpeaker)
this.frenchSpeaker = frenchSpeaker
END CONSTRUCTOR
METHOD speakEnglish(message)
frenchMessage = translateToFrench(message)
frenchSpeaker.speakFrench(frenchMessage)
END METHOD
PRIVATE METHOD translateToFrench(message)
// Simplified translation logic
RETURN message with "Hello" replaced by "Bonjour" and "Thank you" replaced by "Merci"
END METHOD
END CLASS
// Client
CLASS EnglishClient
PRIVATE speaker: EnglishSpeaker
CONSTRUCTOR EnglishClient(speaker: EnglishSpeaker)
this.speaker = speaker
END CONSTRUCTOR
METHOD express(message)
speaker.speakEnglish(message)
END METHOD
END CLASS
// Main
MAIN
frenchSpeaker = NEW FrenchSpeaker()
translator = NEW Translator(frenchSpeaker)
client = NEW EnglishClient(translator)
client.express("Hello! Thank you for the meeting.")
END MAIN
In this example, the Adapter design Pattern is used to bridge the gap between the EnglishClient
(which expects to communicate in English) and the FrenchSpeaker
(which only understands French). The Translator
acts as the adapter, translating English to French, allowing these two components to work together seamlessly. This demonstrates the power of the Adapter Pattern in integrating systems with incompatible interfaces.
FrenchSpeaker
is the Adaptee, providing functionality in French.EnglishSpeaker
is the Target Interface.Translator
is the Adapter, converting English messages to French.EnglishClient
is the Client, using the services ofEnglishSpeaker
.
The Translator
adapts the English messages to French, allowing the English-speaking client to effectively communicate with the French speaker.
Now, we will look at the implementation of this example in different programming languages.
Implementation
package com.ashok.designpatterns.adapter;
/**
*
* @author ashok.mariyala
*
*/
// Adaptee
class FrenchSpeaker {
public void speakFrench(String message) {
System.out.println("Speaking in French: " + message);
}
}
package com.ashok.designpatterns.adapter;
/**
*
* @author ashok.mariyala
*
*/
// Target Interface
interface EnglishSpeaker {
void speakEnglish(String message);
}
package com.ashok.designpatterns.adapter;
/**
*
* @author ashok.mariyala
*
*/
// Adapter
class Translator implements EnglishSpeaker {
private FrenchSpeaker frenchSpeaker;
public Translator(FrenchSpeaker frenchSpeaker) {
this.frenchSpeaker = frenchSpeaker;
}
@Override
public void speakEnglish(String message) {
String frenchMessage = translateToFrench(message);
frenchSpeaker.speakFrench(frenchMessage);
}
private String translateToFrench(String message) {
// Simplified translation logic
return message.replace("Hello", "Bonjour").replace("Thank you", "Merci");
}
}
package com.ashok.designpatterns.adapter;
/**
*
* @author ashok.mariyala
*
*/
// Client
class EnglishClient {
private EnglishSpeaker speaker;
public EnglishClient(EnglishSpeaker speaker) {
this.speaker = speaker;
}
public void express(String message) {
speaker.speakEnglish(message);
}
}
package com.ashok.designpatterns.adapter;
/**
*
* @author ashok.mariyala
*
*/
// Main class to demonstrate the adapter pattern
public class Solution {
public static void main(String[] args) {
FrenchSpeaker frenchSpeaker = new FrenchSpeaker();
Translator translator = new Translator(frenchSpeaker);
EnglishClient client = new EnglishClient(translator);
client.express("Hello! Thank you for the meeting.");
}
}
Application of Adapter Interface
We have understood the concept of the Adapter design Pattern. But, when and where to use it? Let’s discuss some of the applicability scenarios where adapter patterns can be used.
- Incompatible Interfaces: Use it when you have to communicate between two incompatible interfaces.
- Legacy Code Integration: The Adapter Pattern can also be used when you need to reuse some existing or legacy code that can’t be altered. An adapter pattern can help to integrate it with new code.
- Third-party Libraries: If you are using any third-party library that is not compatible with your code, an adapter pattern can help you to bridge this gap.
- Refactoring: When you are refactoring some large codebase, adapters can help in working on new code with the old one very smoothly.
- Multiple Data Formats: If your application needs data from multiple sources and data is in multiple formats, adapters can help in standardising the data in a single format that can seamlessly work with your application.
The Adapter Design Pattern is a powerful tool for ensuring compatibility between interfaces and enhancing the flexibility and reusability of code.
That’s all about the Adapter Design Pattern. If you have any queries or feedback, please write us email at contact@waytoeasylearn.com. Enjoy learning, Enjoy Design patterns.!!