Adapter Design Pattern

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.

Adapter Design Pattern Problem

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!

Adapter Design Pattern Solution

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.

Adapter Design Pattern 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 the Adaptee class. It translates the interface from the Target to something the Adaptee 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 the Client.
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 of EnglishSpeaker.

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.!!

Adapter Design Pattern
Scroll to top