Structural Design Pattern
Definition
The adapter design pattern is one of the structural design patterns and is used so that two unrelated interfaces can work together. The object that joins these unrelated interfaces is called an adapter.
Characteristics
- The Adapter pattern allows you to translate the interface of a class into another interface its clients expect. It helps in enabling classes to work together which could not otherwise because of incompatible interfaces.
- It helps to reduce the complexity by decoupling the client code from the actual implementation of the class.
- It promotes the loose coupling between classes.
- It helps to add functionality to existing classes without making any modifications to the existing code.
- It helps to make the code more extensible.
UML Diagram
Code
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " +
fileName);
}
@Override
public void playMp4(String fileName) {
//do nothing
}
}
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
//do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " +
fileName);
}
}
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
//inbuilt support to play mp3 music files
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " +
fileName);
}
//mediaAdapter is providing support to play other file
formats
else if (audioType.equalsIgnoreCase("vlc") ||
audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. " + audioType + "
format not supported ");
}
}
}
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
Output
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported
Advantages
- It allows us to use code from third parties relatively seamlessly. There’s no need to change existing interfaces to use new code. Just implement an adapter for it.
- Loose coupling and the SRP is enforced in this pattern. A change in either the client interface or the adaptee only means a change to the adapter.
Disadvantages
- It’s sometimes easier to just manually refactor the client interface than it is to use the adapter pattern.
- If you’re using the class adapter pattern, you’ll want to avoid diamond inheritance at all costs. This is unlikely to happen, but if it does, you’ll be in trouble.