21
NovUnderstanding Facade Design Pattern
Facade Design Pattern
Facade Design Pattern is an important concept in software design. It gives a simplified interface to a complicated system of classes, libraries, or frameworks, letting users interact with a single point rather than several intricate subsystems. This pattern keeps the system ordered, lowers complexity, and facilitates management and maintenance.
In this design pattern tutorial, we will look at the Facade Design Pattern and answer the question, "What is the Facade Design Pattern?". Furthermore, "When should I use the Facade Design Pattern?" We'll also examine examples of the Facade Design Pattern in use. So, let's start by addressing the question, "What is the Facade Design Pattern?"
What is a facade Pattern?
- The Facade Design Pattern is a structural design pattern that simplifies interactions between a complex system of classes by providing a unified interface through a facade object.
- Instead of interacting directly with multiple subsystems, clients communicate with the facade, which handles and organizes the underlying processes.
- This reduces the complexity of interactions and promotes easier management and loose coupling between the client and the subsystems.
Real-world Illustration of Facade Design Pattern
- When you use a TV remote, you don't need to operate the internal electronics directly.
- Instead, you just press buttons to change the channel or adjust the volume.
- It’s the remote that simplifies everything by hiding the complexity of the TV's inner workings.
- Without the remote, you would have to manually operate the TV’s controls, which would be more difficult.
- The remote acts as a facade, making it easy for you to control the TV without dealing with the complex systems inside.
Facade Design Pattern Basic Structure and Implementation
The structure for the implementation of the Facade Design Pattern is given below:
The classes, interfaces, and objects in the above structure are as follows:
- Facade: An interface that provides simplified operations for the user by interacting with complex subsystems behind the scenes.
- ConcreteFacade: A class that implements the Facade interface, coordinating and managing the interactions between subsystems.
- Subsystem: Classes representing complex functionalities or operations, such as specific components of a larger system.
- ConcreteSubsystemA/B: Classes that perform complex operations and are accessed through the Facade rather than directly by the client.
Real Life Example:
Subsystems (Home Appliances)
- In a home, it is common to have various appliances like the air conditioner, heater, and lights.
- Each appliance has its own complex controls and settings.
Facade (Home Automation System)
- It is the Home Automation System that acts as a facade.
- Instead of manually adjusting each appliance, you use a single control panel.
Operation Flow
- Simplified Interaction: When you want to set the temperature or turn on the lights, you make these adjustments through the home automation control panel.
- Command Handling: The control panel (facade) sends commands to the appliances. For example, if you set the temperature, it is the control panel that instructs both the heater and the air conditioner.
- Subsystem Response: Each appliance (air conditioner, heater, lights) then responds to these commands. It is the appliances that adjust their settings based on the instructions from the control panel.
How the Facade Helps
- Unified Interface: It is the Home Automation System that provides a single point of control. This means you don’t need to manage each appliance’s complex settings separately.
- Reduced Complexity: By using the facade, it is not necessary to deal with each appliance individually. It simplifies your experience by handling all interactions through one interface.
- Improved Usability: The facade makes it easier to control multiple appliances. It is the control panel that manages everything, making your home environment more convenient and efficient.
Example
using System;
public class AirConditioner
{
public void TurnOn() => Console.WriteLine("Air Conditioner is now ON.");
public void TurnOff() => Console.WriteLine("Air Conditioner is now OFF.");
public void SetTemperature(int temperature) => Console.WriteLine($"Air Conditioner temperature set to {temperature}°C.");
}
public class Heater
{
public void TurnOn() => Console.WriteLine("Heater is now ON.");
public void TurnOff() => Console.WriteLine("Heater is now OFF.");
public void SetTemperature(int temperature) => Console.WriteLine($"Heater temperature set to {temperature}°C.");
}
public class Lights
{
public void TurnOn() => Console.WriteLine("Lights are now ON.");
public void TurnOff() => Console.WriteLine("Lights are now OFF.");
}
public class HomeAutomationFacade
{
private AirConditioner _airConditioner;
private Heater _heater;
private Lights _lights;
public HomeAutomationFacade()
{
_airConditioner = new AirConditioner();
_heater = new Heater();
_lights = new Lights();
}
public void SetTemperature(int temperature)
{
Console.WriteLine("Setting temperature...");
_airConditioner.SetTemperature(temperature);
_heater.SetTemperature(temperature);
}
public void TurnOnHome()
{
Console.WriteLine("Turning on the home system...");
_airConditioner.TurnOn();
_heater.TurnOn();
_lights.TurnOn();
}
public void TurnOffHome()
{
Console.WriteLine("Turning off the home system...");
_airConditioner.TurnOff();
_heater.TurnOff();
_lights.TurnOff();
}
}
class Program
{
static void Main(string[] args)
{
HomeAutomationFacade homeFacade = new HomeAutomationFacade();
homeFacade.TurnOnHome();
homeFacade.SetTemperature(22);
homeFacade.TurnOffHome();
}
}
public class AirConditioner {
public void turnOn() { System.out.println("Air Conditioner is now ON."); }
public void turnOff() { System.out.println("Air Conditioner is now OFF."); }
public void setTemperature(int temperature) { System.out.println("Air Conditioner temperature set to " + temperature + "°C."); }
}
public class Heater {
public void turnOn() { System.out.println("Heater is now ON."); }
public void turnOff() { System.out.println("Heater is now OFF."); }
public void setTemperature(int temperature) { System.out.println("Heater temperature set to " + temperature + "°C."); }
}
public class Lights {
public void turnOn() { System.out.println("Lights are now ON."); }
public void turnOff() { System.out.println("Lights are now OFF."); }
}
public class HomeAutomationFacade {
private AirConditioner airConditioner;
private Heater heater;
private Lights lights;
public HomeAutomationFacade() {
airConditioner = new AirConditioner();
heater = new Heater();
lights = new Lights();
}
public void setTemperature(int temperature) {
System.out.println("Setting temperature...");
airConditioner.setTemperature(temperature);
heater.setTemperature(temperature);
}
public void turnOnHome() {
System.out.println("Turning on the home system...");
airConditioner.turnOn();
heater.turnOn();
lights.turnOn();
}
public void turnOffHome() {
System.out.println("Turning off the home system...");
airConditioner.turnOff();
heater.turnOff();
lights.turnOff();
}
}
public class Main {
public static void main(String[] args) {
HomeAutomationFacade homeFacade = new HomeAutomationFacade();
homeFacade.turnOnHome();
homeFacade.setTemperature(22);
homeFacade.turnOffHome();
}
}
class AirConditioner:
def turn_on(self):
print("Air Conditioner is now ON.")
def turn_off(self):
print("Air Conditioner is now OFF.")
def set_temperature(self, temperature):
print(f"Air Conditioner temperature set to {temperature}°C.")
class Heater:
def turn_on(self):
print("Heater is now ON.")
def turn_off(self):
print("Heater is now OFF.")
def set_temperature(self, temperature):
print(f"Heater temperature set to {temperature}°C.")
class Lights:
def turn_on(self):
print("Lights are now ON.")
def turn_off(self):
print("Lights are now OFF.")
class HomeAutomationFacade:
def __init__(self):
self.air_conditioner = AirConditioner()
self.heater = Heater()
self.lights = Lights()
def set_temperature(self, temperature):
print("Setting temperature...")
self.air_conditioner.set_temperature(temperature)
self.heater.set_temperature(temperature)
def turn_on_home(self):
print("Turning on the home system...")
self.air_conditioner.turn_on()
self.heater.turn_on()
self.lights.turn_on()
def turn_off_home(self):
print("Turning off the home system...")
self.air_conditioner.turn_off()
self.heater.turn_off()
self.lights.turn_off()
if __name__ == "__main__":
home_facade = HomeAutomationFacade()
home_facade.turn_on_home()
home_facade.set_temperature(22)
home_facade.turn_off_home()
class AirConditioner {
turnOn() { console.log("Air Conditioner is now ON."); }
turnOff() { console.log("Air Conditioner is now OFF."); }
setTemperature(temperature: number) { console.log(`Air Conditioner temperature set to ${temperature}°C.`); }
}
class Heater {
turnOn() { console.log("Heater is now ON."); }
turnOff() { console.log("Heater is now OFF."); }
setTemperature(temperature: number) { console.log(`Heater temperature set to ${temperature}°C.`); }
}
class Lights {
turnOn() { console.log("Lights are now ON."); }
turnOff() { console.log("Lights are now OFF."); }
}
class HomeAutomationFacade {
private airConditioner: AirConditioner;
private heater: Heater;
private lights: Lights;
constructor() {
this.airConditioner = new AirConditioner();
this.heater = new Heater();
this.lights = new Lights();
}
setTemperature(temperature: number) {
console.log("Setting temperature...");
this.airConditioner.setTemperature(temperature);
this.heater.setTemperature(temperature);
}
turnOnHome() {
console.log("Turning on the home system...");
this.airConditioner.turnOn();
this.heater.turnOn();
this.lights.turnOn();
}
turnOffHome() {
console.log("Turning off the home system...");
this.airConditioner.turnOff();
this.heater.turnOff();
this.lights.turnOff();
}
}
// Client code
const homeFacade = new HomeAutomationFacade();
homeFacade.turnOnHome();
homeFacade.setTemperature(22);
homeFacade.turnOffHome();
class AirConditioner {
turnOn() { console.log("Air Conditioner is now ON."); }
turnOff() { console.log("Air Conditioner is now OFF."); }
setTemperature(temperature) { console.log(`Air Conditioner temperature set to ${temperature}°C.`); }
}
class Heater {
turnOn() { console.log("Heater is now ON."); }
turnOff() { console.log("Heater is now OFF."); }
setTemperature(temperature) { console.log(`Heater temperature set to ${temperature}°C.`); }
}
class Lights {
turnOn() { console.log("Lights are now ON."); }
turnOff() { console.log("Lights are now OFF."); }
}
class HomeAutomationFacade {
constructor() {
this.airConditioner = new AirConditioner();
this.heater = new Heater();
this.lights = new Lights();
}
setTemperature(temperature) {
console.log("Setting temperature...");
this.airConditioner.setTemperature(temperature);
this.heater.setTemperature(temperature);
}
turnOnHome() {
console.log("Turning on the home system...");
this.airConditioner.turnOn();
this.heater.turnOn();
this.lights.turnOn();
}
turnOffHome() {
console.log("Turning off the home system...");
this.airConditioner.turnOff();
this.heater.turnOff();
this.lights.turnOff();
}
}
// Client code
const homeFacade = new HomeAutomationFacade();
homeFacade.turnOnHome();
homeFacade.setTemperature(22);
homeFacade.turnOffHome();
Output
Turning on the home system...
Air Conditioner is now ON.
Heater is now ON.
Lights are now ON.
Setting temperature...
Air Conditioner temperature set to 22°C.
Heater temperature set to 22°C.
Turning off the home system...
Air Conditioner is now OFF.
Heater is now OFF.
Lights are now OFF.
Explanation
- This code demonstrates the Facade Design Pattern through a home automation system.
- It is the HomeAutomationFacade that simplifies the control of multiple subsystems like AirConditioner, Heater, and Lights.
- Instead of interacting with each appliance separately, it is possible to manage everything with a single interface.
- This makes it easier to control home appliances through a few simple commands.
Applications of Facade Design Pattern
Use the Facade pattern when you require a simple interface to a complex subsystem.
- Subsystems frequently evolve into more sophisticated structures throughout time.
- Even applying design patterns often results in the creation of new classes.
- A subsystem may become more adaptable and easier to reuse in multiple situations, but the amount of configuration and boilerplate code required from a client increases.
- The Facade seeks to address this issue by offering a shortcut to the subsystem's most often-used features, which meet the majority of client requirements.
Use the Facade to divide a subsystem into layers.
- Create facades to define the entry points for each level of a subsystem.
- To eliminate connectivity between several subsystems, they must be required to communicate exclusively through facades.
- Let's get back to our video conversion framework.
- It can be divided into two layers: video and audio-related.
- You can create a facade for each layer and then use those facades to connect with the classes in that layer. This method is quite similar to the Mediator pattern.
Benefits of Facade Design Pattern
- It simplifies the interaction with complex subsystems.
- It provides a unified interface for easier control.
- It hides the complexity of multiple subsystems from the client.
- Because subsystems are accessed through the facade, it is easier to manage and maintain them.
- The client code becomes cleaner and more straightforward since it does not require detailed knowledge of subsystem operations.
When to use the Facade Method Design Pattern
- A Facade provides a simple default view of the subsystem that is suitable for the majority of clients.
- Only consumers who require greater customization will need to look beyond the facade.
- Clients and an abstraction's implementation classes are highly interdependent.
- A facade that isolates the subsystem from customers and other subsystems, encouraging subsystem independence and portability.
- Facades define an entry point into each subsystem level.
- If subsystems are dependent, you can reduce their dependencies by limiting their communication to their facades.
When not to use the Facade Method Design Pattern
- It is unnecessary to use the Facade pattern when the system lacks multiple complex subsystems, as it can add unneeded abstraction.
- In small-scale applications, where direct access to components is efficient, a facade might create unnecessary overhead.
- It can also violate the Single Responsibility Principle by grouping different operations in one interface.
- If using a facade complicates rather than simplifies the system, it is likely over-engineering and should be avoided for straightforward use cases.