Both the Adapter pattern and Facade pattern are used to simplify the interface of existing classes or subsystems.
The Adapter pattern is used when we have an existing class that provides an interface that is different from the one we need. We use an Adapter to wrap the existing class and provide the interface that our client code needs. The Adapter has a reference to the existing class and translates the client’s requests into calls to the appropriate methods on the existing class. In other words, the Adapter pattern is used to make two incompatible interfaces compatible with each other.
On the other hand, the Facade pattern is used when we have a complex subsystem that we want to simplify for our client code. We create a Facade object that provides a simplified interface to the client code. The Facade object delegates the client’s requests to the appropriate classes in the subsystem. In other words, the Facade pattern is used to provide a unified interface to a set of interfaces in a subsystem, thus simplifying its usage.
So, when choosing between the Adapter pattern and Facade pattern, we need to consider the following:
1. If we have an existing class that we want to reuse, but its interface is incompatible with our client code, then we should use the Adapter pattern.
2. If we have a complex subsystem with multiple interfaces and we want to simplify its usage for our client code, then we should use the Facade pattern.
Here is an example of how the Adapter pattern can be used in Java:
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{
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
public void playMp4(String fileName) {
// do nothing
}
}
public class Mp4Player implements AdvancedMediaPlayer{
public void playVlc(String fileName) {
// do nothing
}
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();
}
}
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;
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: " + fileName);
}
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");
}
}
In the above example, we have an AudioPlayer that can play mp3 files natively. However, if it receives a request to play a file of type vlc or mp4, it uses the MediaAdapter to convert the request to a format that it can handle natively.
Here is an example of how the Facade pattern can be used in Java:
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
public void draw() {
System.out.println("Rectangle::draw()");
}
}
public class Square implements Shape {
public void draw() {
System.out.println("Square::draw()");
}
}
public class Circle implements Shape {
public void draw() {
System.out.println("Circle::draw()");
}
}
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
public class FacadePatternDemo {
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawSquare();
}
}
In the above example, we have a ShapeMaker that simplifies the interface to a set of Shape classes. The client code only needs to call methods on the ShapeMaker and doesn’t need to know about the individual Shape classes. This simplifies the usage of the Shape classes for the client code.