This article is lecture note from "Head First Design Patterns" by Elisabeth Freeman and Kathy Sierra and CS course from Kookmin by Inkyu Kim
The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher level of interface that makes the subsystem easier to use. Facade wraps a complicated subsystem with a simpler interface. This type of design pattern comes under structural pattern as this pattern adds an interface to existing system to hide its complexities.
This pattern involves a single class which provides simplified methods required by client and delegates calls to methods of existing system classes.
The Facade provides convenient access to a particular part of the subsystems's functionality. It knows where to direct the client's request and how to operate all the moving parts.
An Additional Facade class can be created to prevent polluting a single facade with unrelated features that might make it yet another complex structure. Additional facades can be used by both clients and other facades.
The Complex Subsystem consists of dozens of various objects. To make them all do something meaningful, you have to dive deep into the subsystems's implementation details, such as initialising objects in the correct order and supplying them with data in the proper format.
Subsystem classes aren't aware of the facade's existence. They operate within the system and work with each other directly. The Client uses the facade instead of calling the subsystem objects directly.
Here is a example of a Facade Pattern which is remote control system making home theatre.
<HomeTheaterFacade.java>
public class HomeTheaterFacade {
Amplifier amp;
Tuner tuner;
StreamingPlayer player;
CdPlayer cd;
Projector projector;
TheaterLights lights;
Screen screen;
PopcornPopper popper;
public HomeTheaterFacade(Amplifier amp,
Tuner tuner,
StreamingPlayer player,
Projector projector,
Screen screen,
TheaterLights lights,
PopcornPopper popper) {
this.amp = amp;
this.tuner = tuner;
this.player = player;
this.projector = projector;
this.screen = screen;
this.lights = lights;
this.popper = popper;
}
public void watchMovie(String movie) {
System.out.println("Get ready to watch a movie...");
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amp.on();
amp.setStreamingPlayer(player);
amp.setSurroundSound();
amp.setVolume(5);
player.on();
player.play(movie);
}
public void endMovie() {
System.out.println("Shutting movie theater down...");
popper.off();
lights.on();
screen.up();
projector.off();
amp.off();
player.stop();
player.off();
}
public void listenToRadio(double frequency) {
System.out.println("Tuning in the airwaves...");
tuner.on();
tuner.setFrequency(frequency);
amp.on();
amp.setVolume(5);
amp.setTuner(tuner);
}
public void endRadio() {
System.out.println("Shutting down the tuner...");
tuner.off();
amp.off();
}
}
As we can see from the above code, the facade has all the components of the subsystem we are going to use. (Amplifier, Tuner, DvdPlayer .. etc). The Facade is passed a referenceto each component of the subsystem in its constructor. The Facade then assigns each to the corresponding instance variable.
Let's look at the watchMovie() method. It follows the same sequence we had to do by hand before, but wraps it up in a handy method that does all the work. Notice that for each task we are delegating the responsibility to the corresponding component in the subsystem.
<HomeTheaterTestDrive.java>
public class HomeTheaterTestDrive {
public static void main(String[] args) {
Amplifier amp = new Amplifier("Amplifier");
Tuner tuner = new Tuner("AM/FM Tuner", amp);
StreamingPlayer player = new StreamingPlayer("Streaming Player", amp);
CdPlayer cd = new CdPlayer("CD Player", amp);
Projector projector = new Projector("Projector", player);
TheaterLights lights = new TheaterLights("Theater Ceiling Lights");
Screen screen = new Screen("Theater Screen");
PopcornPopper popper = new PopcornPopper("Popcorn Popper");
HomeTheaterFacade homeTheater =
new HomeTheaterFacade(amp, tuner, player,
projector, screen, lights, popper);
homeTheater.watchMovie("Raiders of the Lost Ark");
homeTheater.endMovie();
}
}
Here we're creating the components right in the test drive. Normally, the client is given a facade, it doesn't have to construct one itself.
First we instantiate the Facade with all the components in the subsystem.
At the end, use the simplified interface to first start the movie up, and then shut it down.
A Facade is a class that provides a simple interface to a complex subsystem which contains lots of moving parts. In other words, you can isolate your code from the complexity of a subsystem. A Facade might prove limited functionality in comparison to working with the subsystem directly. However, it includes only those features that clients really care about.
For instance, an app that uploads short videos with cats to social media could potentially use a professional video conversion library. However, all that it really needs is a class with the single method encode(filename, format). After creating such a class and connecting it with the video conversion library, you'll have your first facade.
More important thing is that this pattern fulfill the Principle of Least Knowledge. When we are designing a system, for any object, be careful of the number of classes it interacts with and how it comes to interact with those classes. This principle prevent us from creating designs that have a large number of classes coupled together so that changes in one part of the system cascade to other parts. When you build a lot of dependencies between many classes, you are building a fragile system that will be costly to maintain and complex for others to understand.