TeachingBee

Adapter Design Pattern : Low Level Design

adapter design pattern

The Adapter Design Pattern, also known as the Wrapper pattern, is a structural design pattern that allows objects with incompatible interfaces to collaborate. It acts as a bridge between two incompatible interfaces by converting the interface of a class into another interface that a client expects.

Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

What is Adapter Design Pattern?

The Adapter Design Pattern is a structural design pattern that allows objects with incompatible interfaces to collaborate. This pattern acts as a bridge between two incompatible interfaces by converting the interface of one class into another interface that the client expects. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

The primary purpose of this pattern is to enable the reuse of existing code without modifying it, even if it does not match the required interfaces. This is particularly useful when integrating new features or libraries into existing systems without altering the existing codebase.

Imagine you are developing a media player application that initially only supports playing MP3 files. Over time, you decide to add support for more audio formats like MP4 and VLC. However, the existing system’s architecture only accommodates the MP3 format.

// Define the MediaPlayer interface with a play method.
Interface MediaPlayer
    Method play(audioType As String, fileName As String)
EndInterface

// Define the Mp3Player class that implements the MediaPlayer interface.
Class Mp3Player Implements MediaPlayer
    // Implement the play method to handle mp3 files.
    Method play(audioType As String, fileName As String)
        // Check if the audio type is MP3.
        If audioType.equalsIgnoreCase("mp3") Then
            // If the audio type is MP3, print a message indicating the file is being played.
            Print "Playing mp3 file. Name: " + fileName
        Else
            // If the audio type is not MP3, print an error message.
            Print "Invalid media. " + audioType + " format not supported"
        EndIf
    EndMethod
EndClass

Adding direct support for additional formats would require modifying the MediaPlayer interface and its existing implementations, violating the Open/Closed Principle.

Solving the Issue with Adapter Design Pattern

The Adapter Design Pattern can solve this problem by allowing the media player to use classes that support playing other formats without changing the player’s code.

How to Implement Adapter Design Pattern?

To Implement adapter design pattern let’s look at components involved in adapter design pattern and apply these in above problem.

Components of the Adapter Design Pattern

Adapter Design Pattern

Adapter Design Pattern can be implemented by using following components:

Target Interface

The Target Interface defines a standard way for the client (in this case, the media player application) to interact with audio files. It specifies the operations that can be performed on audio files, such as playing, pausing, and stopping, which are implemented with the expectation of dealing with MP3 files.

For Example:

In the media player scenario, MP3PlayerInterface is the interface originally designed for playing MP3 files. The media player’s components use this interface to control audio playback. It serves as the expected interface for audio file interactions within the application.

Interface MP3PlayerInterface
    Method play()
    Method pause()
    Method stop()
EndInterface

Adaptee Interface

The Adaptee Interface or Class represents the functionality necessary to support additional audio formats but does not conform to the Target Interface. Each Adaptee has its methods and capabilities tailored to a specific audio format, such as MP4 or VLC, which differ from the expected operations defined in the Target Interface.

For Example:

  • MP4Player (Adaptee 1) provides the functionality to play MP4 files but through a different interface than the MP3PlayerInterface. This discrepancy necessitates adaptation to be usable by the media player application.
  • VLCPlayer (Adaptee 2) similarly handles VLC file formats. Its interface and methods for controlling playback differ from both the MP3PlayerInterface and MP4Player, representing another instance where adaptation is required.
Class MP4Player
    Method playFile()
        // Implementation for playing an MP4 file
    EndMethod

    Method pauseFile()
        // Implementation for pausing MP4 playback
    EndMethod

    Method stopFile()
        // Implementation for stopping MP4 playback
    EndMethod
EndClass
Class VLCPlayer
    Method start()
        // Implementation for starting VLC file playback
    EndMethod

    Method halt()
        // Implementation for pausing VLC file playback
    EndMethod

    Method end()
        // Implementation for stopping VLC file playback
    EndMethod
EndClass

Adapter

The Adapter implements the Target Interface and contains a reference to an Adaptee object. It translates or adapts the interface of the Adaptee to the Target Interface. This involves taking the methods defined by the Target Interface and implementing them in such a way that they call the corresponding methods of the Adaptee.

For Example:

  • MP4PlayerAdapter wraps an instance of MP4Player, implementing the MP3PlayerInterface. It translates the generic play, pause, and stop commands into the specific commands required by MP4Player, enabling the media player application to control MP4 files using the familiar interface designed for MP3 files.
  • VLCPlayerAdapter functions similarly but for VLCPlayer. It adapts the VLC-specific playback controls to match the MP3PlayerInterface. This allows the media player application to seamlessly interact with VLC files through the same interface used for MP3 files, ensuring consistency across different file formats.
Class MP4PlayerAdapter implements MP3PlayerInterface
    Private MP4Player mp4Player;

    // Constructor to initialize the adapter with an MP4Player
    MP4PlayerAdapter(MP4Player player)
        this.mp4Player = player;
    EndConstructor

    // Implementation of the play method from MP3PlayerInterface
    Method play()
        // Call the playFile method of the MP4Player
        mp4Player.playFile();
    EndMethod

    // Implementation of the pause method from MP3PlayerInterface
    Method pause()
        // Call the pauseFile method of the MP4Player
        mp4Player.pauseFile();
    EndMethod

    // Implementation of the stop method from MP3PlayerInterface
    Method stop()
        // Call the stopFile method of the MP4Player
        mp4Player.stopFile();
    EndMethod
EndClass
Class VLCPlayerAdapter implements MP3PlayerInterface
    Private VLCPlayer vlcPlayer;

    // Constructor to initialize the adapter with a VLCPlayer
    VLCPlayerAdapter(VLCPlayer player)
        this.vlcPlayer = player;
    EndConstructor

    // Implementation of the play method from MP3PlayerInterface
    Method play()
        // Call the start method of the VLCPlayer
        vlcPlayer.start();
    EndMethod

    // Implementation of the pause method from MP3PlayerInterface
    Method pause()
        // Call the halt method of the VLCPlayer
        vlcPlayer.halt();
    EndMethod

    // Implementation of the stop method from MP3PlayerInterface
    Method stop()
        // Call the end method of the VLCPlayer
        vlcPlayer.end();
    EndMethod
EndClass
Adapter Design Pattern Example

Usage:

// Instantiate MP4 player and VLC player objects
MP4Player mp4Player = new MP4Player();
VLCPlayer vlcPlayer = new VLCPlayer();

// Create adapters for MP4 and VLC players
MP4PlayerAdapter mp4Adapter = new MP4PlayerAdapter(mp4Player);
VLCPlayerAdapter vlcAdapter = new VLCPlayerAdapter(vlcPlayer);

// Use MP4 player adapter to play, pause, and stop MP4 file
mp4Adapter.play();
mp4Adapter.pause();
mp4Adapter.stop();

// Use VLC player adapter to play, pause, and stop VLC file
vlcAdapter.play();
vlcAdapter.pause();
vlcAdapter.stop();

Common Usage of the Adapter Design Pattern

The Adapter Pattern is commonly used in:

  1. Integrating New Libraries or Systems: When adding new libraries or systems that have different interfaces from the rest of the application.
  2. Interface Conversion: When needing to convert data into a format that is more useful for the consumer.
  3. Legacy Integration: When working with legacy code or systems that cannot be modified directly but need to be integrated with newer systems.

Identifying Where To Apply Adapter Design Pattern

  • Incompatible Interfaces: When two interfaces that perform similar functions but have different signatures need to be integrated.
  • Extension without Modification: When an existing class needs to be extended to match a new interface without modifying its source code.
  • Unified Interfaces: When there’s a need to provide a unified interface to a set of interfaces in a subsystem.

Benefits of the Adapter Design Pattern

  1. Reusability and Flexibility: Allows for the reuse of existing code even if it does not match the target interface, enhancing flexibility.
  2. Decoupling: The client is decoupled from the specific implementation of the interface it uses, promoting loose coupling.
  3. Simplifies the Client Interface: Clients can work with a simple, consistent interface even when interacting with classes with different interfaces.

DrawBacks of the Adapter Design Pattern

  1. Complexity: Can increase the overall complexity of the code by introducing additional layers and potentially more boilerplate code.
  2. Performance: Might introduce a small overhead due to the additional indirection when calling methods through the adapter.
  3. Understanding and Maintenance: Requires a good understanding of both the target and adaptee interfaces, which can make the system harder to understand and maintain.

Key TakeAways

  • Adapter pattern allows different classes with incompatible interfaces to work together seamlessly by providing a common interface.
  • It enables integration of existing components without altering their source code, promoting code reusability and minimizing refactoring efforts.
  • Adapters promote loose coupling between components, allowing for easier maintenance and modification as system requirements evolve.
  • By encapsulating complexities, adapters shield clients from implementation details, ensuring clear separation of concerns and improving code readability.

Similar Posts

What Is the Difference Between Factory and Abstract Factory Design Patterns?

When to Use Factory Design Pattern in Java?

Builder Design Pattern

Factory Design Pattern

FAQ

What is an example of an adapter design pattern in real life?

What is the adapter design template for?

What does Adapter pattern describe?

What is the use of adapter?

90% of Tech Recruiters Judge This In Seconds! 👩‍💻🔍

Don’t let your resume be the weak link. Discover how to make a strong first impression with our free technical resume review!

Related Articles

Types of Memory and Storage in system design thumbnail

Types of Computer Memory and Storage

In this article we will look into different types of computer memory, distinguishing between primary memory and secondary memory. We will also see their characteristics, major kinds, usage, and key

latency in system design thumbnail

What is Latency In System Design?

In this article we will look into Latency In System Design, we will see how is latency introduced into the systems and what are various ways to reduce the latency

Why Aren’t You Getting Interview Calls? 📞❌

It might just be your resume. Let us pinpoint the problem for free and supercharge your job search. 

Newsletter

Don’t miss out! Subscribe now

Log In