What is Adapter Pattern?

The Adapter pattern is a structural design pattern that allows objects with incompatible interfaces to work together by creating an adapter that acts as a bridge between them. It converts the interface of one class into another interface that clients expect, enabling them to collaborate without needing to modify their code.

The Adapter pattern is useful when you want to reuse existing classes or libraries that have incompatible interfaces or when you need to integrate legacy code with new code that expects a different interface. By creating an adapter, you can make these incompatible classes or interfaces work together seamlessly.

Key components of the Adapter pattern:

  1. Target: The target is the interface or class that the client code expects to interact with. It defines the methods or operations that the client code can use.
  2. Adaptee: The adaptee is the existing class or interface that needs to be adapted. It has an incompatible interface that the client cannot directly use.
  3. Adapter: The adapter is the bridge that connects the client code to the adaptee. It implements the target interface and internally wraps an instance of the adaptee. It translates the requests from the client code to the corresponding calls on the adaptee’s interface.

Benefits and use cases of the Adapter pattern:

  1. Seamless integration of incompatible components: The Adapter pattern allows you to integrate existing or third-party components with incompatible interfaces into your application without modifying the client code. You can create adapters that adapt the interfaces of these components to match the expected interface of the client.
  2. Reuse of existing code: The Adapter pattern enables you to reuse existing classes or libraries that have useful functionality but incompatible interfaces. Rather than rewriting or modifying the existing code, you can create adapters to make them compatible with your application.
  3. Separation of concerns: Adapters separate the concerns of the client code and the adaptee. The client code only interacts with the adapter, which encapsulates the details of working with the adaptee. This promotes a clear separation of responsibilities and enhances code maintainability.
  4. Flexibility and extensibility: The Adapter pattern allows for flexibility and extensibility in the system. New adapters can be created to adapt additional classes or interfaces without affecting the existing client code. This promotes modularity and supports future changes or additions to the system.
  5. Legacy code integration: The Adapter pattern is often used to integrate legacy code or systems with modern applications. By creating adapters that translate the modern interface to the legacy interface, you can smoothly integrate the legacy code without needing to modify it.

Example of the Adapter pattern in Java:

// Target interface
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

// Adaptee interface
public interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

// Adaptee implementation 1
public class VlcPlayer implements AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        System.out.println("Playing Vlc file: " + fileName);
    }

    public void playMp4(String fileName) {
        // Do nothing
    }
}

// Adaptee implementation 2
public class Mp4Player implements AdvancedMediaPlayer {
    public void playVlc(String fileName) {
        // Do nothing
    }

    public void playMp4(String fileName) {
        System.out.println("Playing Mp4 file: " + fileName);
    }
}

// Adapter
public class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer = new Mp4Player();
        }
    }

    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer.playMp4(fileName);
        }
    }
}

// Client code
public class AudioPlayer implements MediaPlayer {
    private MediaAdapter mediaAdapter;

    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing Mp3 file: " + fileName);
        } else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media type: " + audioType);
        }
    }
}

// Usage:
MediaPlayer audioPlayer = new AudioPlayer();

audioPlayer.play("mp3", "song.mp3");
audioPlayer.play("vlc", "movie.vlc");
audioPlayer.play("mp4", "video.mp4");

In the above example, the Adapter pattern is used to make different audio and video players work together. The MediaPlayer interface represents the target interface that the client code expects. The AdvancedMediaPlayer interface represents the incompatible interface implemented by the specific media player classes.

The VlcPlayer and Mp4Player classes are the adaptee implementations. They provide functionality for playing Vlc and Mp4 files, respectively.

The MediaAdapter class acts as the adapter. It implements the MediaPlayer interface and internally wraps an instance of the AdvancedMediaPlayer. It translates the requests from the client code to the appropriate calls on the adaptee’s interface.

The AudioPlayer class represents the client code. It interacts with the media players through the MediaPlayer interface. When a non-Mp3 file is encountered, it uses the MediaAdapter to play the file by adapting it to the appropriate media player.

By creating the adapter and implementing the target interface, the incompatible media player classes can work seamlessly with the client code, allowing the client to play different types of media files without worrying about the specific player implementations.

error: Content is protected !!