1. Introduction
In this quick tutorial, we’re going to take a look at one of the structural design patterns: the Facade.
First, we’ll give an overview of the pattern, list its benefits and describe what problems it solves.
Then, we’ll apply the facade pattern to an existing, practical problem with Java.
2. What Is a Facade?
Simply put, a facade encapsulates a complex subsystem behind a simple interface. It hides much of the complexity and makes the subsystem easy to use.
Also, if we need to use the complex subsystem directly, we still can do that; we aren’t forced to use the facade all the time.
Besides a much simpler interface, there’s one more benefit of using this design pattern. It decouples a client implementation from the complex subsystem. Thanks to this, we can make changes to the existing subsystem and don’t affect a client.
Let’s see the facade in action.
3. Example
Let’s say that we want to start a car. The following diagram represents the legacy system, which allows us to do so:
As you can see, it can be quite complex and does require some effort to start the engine correctly:
airFlowController.takeAir()
fuelInjector.on()
fuelInjector.inject()
starter.start()
coolingController.setTemperatureUpperLimit(DEFAULT_COOLING_TEMP)
coolingController.run()
catalyticConverter.on()
Similarly, stopping the engine also requires quite a few steps:
fuelInjector.off()
catalyticConverter.off()
coolingController.cool(MAX_ALLOWED_TEMP)
coolingController.stop()
airFlowController.off()
A facade is just what we need here. We’ll hide all the complexity in two methods: startEngine() and stopEngine().
Let’s see how we can implement it:
public class CarEngineFacade {
private static int DEFAULT_COOLING_TEMP = 90;
private static int MAX_ALLOWED_TEMP = 50;
private FuelInjector fuelInjector = new FuelInjector();
private AirFlowController airFlowController = new AirFlowController();
private Starter starter = new Starter();
private CoolingController coolingController = new CoolingController();
private CatalyticConverter catalyticConverter = new CatalyticConverter();
public void startEngine() {
fuelInjector.on();
airFlowController.takeAir();
fuelInjector.on();
fuelInjector.inject();
starter.start();
coolingController.setTemperatureUpperLimit(DEFAULT_COOLING_TEMP);
coolingController.run();
catalyticConverter.on();
}
public void stopEngine() {
fuelInjector.off();
catalyticConverter.off();
coolingController.cool(MAX_ALLOWED_TEMP);
coolingController.stop();
airFlowController.off();
}
Now, to start and stop a car, we need only 2 lines of code, instead of 13:
facade.startEngine();
// ...
facade.stopEngine();
4. Drawbacks
The facade pattern doesn’t force us to unwanted tradeoffs, because it only adds additional layers of abstraction.
Sometimes the pattern can be overused in simple scenarios, which will lead to redundant implementations.
5. Conclusion
In this article, we’ve explained the facade pattern and demonstrated how to implement it atop of an existing system.
The implementation of these examples can be found over on GitHub.