Table of Contents
ToggleThe 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 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
andMP4Player
, 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 theMP3PlayerInterface
. It translates the generic play, pause, and stop commands into the specific commands required byMP4Player
, 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 theMP3PlayerInterface
. 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
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:
- Integrating New Libraries or Systems: When adding new libraries or systems that have different interfaces from the rest of the application.
- Interface Conversion: When needing to convert data into a format that is more useful for the consumer.
- 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
- Reusability and Flexibility: Allows for the reuse of existing code even if it does not match the target interface, enhancing flexibility.
- Decoupling: The client is decoupled from the specific implementation of the interface it uses, promoting loose coupling.
- 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
- Complexity: Can increase the overall complexity of the code by introducing additional layers and potentially more boilerplate code.
- Performance: Might introduce a small overhead due to the additional indirection when calling methods through the adapter.
- 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?
FAQ
What is an example of an adapter design pattern in real life?
A real-life example of the Adapter design pattern is a power adapter. Consider traveling to a different country where electrical outlets have different shapes or voltages. A power adapter serves as an intermediary between your electronic devices (which expect a specific type of plug) and the foreign electrical outlet, allowing you to connect and use your devices without needing to modify them. The adapter converts the interface of the electrical outlet to match the interface expected by your devices.
What is the adapter design template for?
The Adapter design pattern template provides a blueprint for implementing adapters that allow incompatible interfaces to work together. It typically involves defining an adapter class that wraps around an incompatible class or interface, providing a compatible interface that other parts of the system can use seamlessly.
What does Adapter pattern describe?
The Adapter pattern describes a structural design pattern that allows objects with incompatible interfaces to collaborate. It acts as a bridge between two incompatible interfaces, enabling them to work together without modifying their original code. The adapter translates the interface of one class into another interface that clients expect, allowing objects to interact with each other.
What is the use of adapter?
The main use of the Adapter pattern is to enable communication and collaboration between classes or systems with incompatible interfaces. It allows existing classes to work with new or external classes without modifying their code. Adapters are particularly useful when integrating legacy code with modern systems, integrating third-party libraries, or providing a uniform interface for interacting with multiple classes or systems.