Facade Pattern in C++
In this article, we will discuss the façade pattern in C++ with examples.
Introduction to the Facade Pattern
A structural design pattern called the facade pattern that gives a group of interfaces in a subsystem a cohesive interface. It defines a higher-level interface that facilitates the use of the subsystem.
The Facade Pattern is designed to make it easier for clients to interface with complicated subsystems. Instead of explicitly requiring the client to engage with each of the subsystem's interfaces, it accomplishes this by offering a single interface to the subsystem.
Useful Circumstances for the Facade Pattern:
The Facade Pattern is helpful in a variety of circumstances, such as:
- Whenever the subsystem is complex and contains numerous interfaces, managing its components and interactions is essential.
- When the client needs protection from changes in the subsystem, even though it is prone to change, implementing stable interfaces and abstractions is crucial to shield it from potential disruptions.
- The facade can offer a streamlined user interface when a client needs to access only a portion of a subsystem's functionality.
Facade Pattern:
A straightforward example of the C++ facade pattern is shown in the code below:
#include <iostream>
class Engine {
public:
void Start() {
// Code to start the engine.
}
};
class Lights {
public:
void TurnOn() {
// Code to turn on the lights.
}
};
class Car {
public:
Car(Engine& engine, Lights& lights)
: engine_(engine), lights_(lights) {}
void StartCar() {
engine_.Start();
lights_.TurnOn();
}
private:
Engine& engine_;
Lights& lights_;
};
int main() {
Engine carEngine;
Lights carLights;
Car myCar(carEngine, carLights);
// The client can now use the car facade to start the car with lights on
myCar.StartCar();
return 0;
}
In this illustration, the Facade class offers the Subsystem1 and Subsystem2 classes a uniform interface. Now, the client can communicate with the subsystem using the Facade class without knowing how it is internally implemented.
Use of the Facade Pattern Benefits:
Numerous advantages of the facade pattern include:
- It makes it easier for a client to communicate with a complicated subsystem.
- It shields the client from subsystem modifications.
- It makes using and maintaining the subsystem simpler.
Design ideas that underlie the facade pattern:
Following are the design tenets around which The Facade Pattern is built:
- Encapsulation: Encapsulation exposes only a class's public interface and conceals its implementation specifics. Instead of directly exposing the subsystem's internal interfaces, the facade pattern encapsulates the subsystem by offering a single interface.
- Abstraction: Abstraction is the process of obfuscating superfluous information and highlighting necessary information. The facade pattern abstracts away the subsystem's complexity by offering a streamlined interface to the subsystem.
How the Pattern of the Facade Encourages Loose Coupling:
- According to the design idea of "loose coupling", system parts should be only weakly related. This design principle implies that dependencies between components should be minimized, allowing for greater flexibility and ease of maintenance.
- The facade pattern encourages loose coupling between clients and subsystems by giving the subsystem a single interface. This design pattern implies that neither the client nor the internal implementation of the subsystem must be known to one another, promoting a high level of abstraction and separation, which can simplify system development and enhance modularity.
- It increases the system's flexibility and maintainability because both the client and the subsystem can be changed without affecting the other.
Example of Loose Coupling:
An instance of loose coupling in C++ utilizing the Facade Pattern is shown in the code below:
#include <iostream>
class Engine {
public:
void Start() {
// Code to start the engine.
}
};
class Lights {
public:
void TurnOn() {
// Code to turn on the lights.
}
};
class CarFacade {
public:
CarFacade(Engine& engine, Lights& lights)
: engine_(engine), lights_(lights) {}
void StartCar() {
engine_.Start();
lights_.TurnOn();
}
private:
Engine& engine_;
Lights& lights_;
};
class CarDriver {
public:
CarDriver(CarFacade& car) : car_(car) {}
void Drive() {
car_.StartCar();
}
private:
CarFacade& car_;
};
int main() {
Engine carEngine;
Lights carLights;
CarFacade car(carEngine, carLights);
CarDriver driver(car);
// The client (driver) can now use the car facade to start the car with lights on.
driver.Drive();
return 0;
}
Explanation:
In this modified example, the CarDriver class is only tangentially related to the Engine and Lights classes. The internal implementation of the Engine and Lights classes does not need to be known by the CarDriver class, and vice versa for the Engine and Lights classes.
The CarDriver class can communicate with the subsystem through the CarFacade class, which combines the Engine and Lights classes into a single interface for the subsystem.
This layout encourages adaptability and upkeep. It is possible to modify both the Engine and Lights classes without changing the CarDriver class and the CarDriver class itself without changing the Engine and Lights classes. The client's contact with the vehicle's subsystem is made simpler, making future updates and modifications simpler.
Real-World Examples
Here are some instances in real life where and why you would want to employ the C++ facade pattern:
- The use of a library or framework can be made simpler by using the Facade Pattern. For instance, the Java Database Connectivity (JDBC) API provides a Facade to the underlying database system. As a result, developers may utilize the JDBC API more easily without being familiar with the specifics of the underlying database system.
- In an architecture based on microservices, using the facade pattern can simplify communication between microservices. For example, each microservice can provide its Facade, presenting a streamlined interface to its functionality. It enables additional microservices to connect more seamlessly without needing to understand the implementation details of each microservice.
- The responsibility for the functionality provided by the Facade class falls upon the subsystem classes in the Facade Pattern.
- The Facade Pattern can streamline communication between microservices in a microservices architecture. For instance, each microservice may offer a Facade that exposes a condensed interface to its functionality. It makes it simpler for other microservices to connect since they no longer need to be aware of the specifics of how each microservice is implemented.
Subsystem Classes:
The subsystem classes are frequently complicated and have a wide range of duties. On the other hand, the Facade class wraps together the subsystem classes and offers a streamlined interface to their functionality.
By calling the proper methods on the subsystem classes, the Facade class assigns duties to the subsystems. For instance, if the client uses the Facade class's DoSomething() method, the Facade class might call the Subsystem1 class's DoSomething() method and the Subsystem2 class's DoSomething2() method.
Example:
Here is an illustration of how the C++ Facade class could assign duties to the subsystems:
#include <iostream>
class Engine {
public:
void Start() {
// Code to start the engine.
std::cout << "Engine started." << std::endl;
}
};
class Lights {
public:
void TurnOn() {
// Code to turn on the lights.
std::cout << "Lights turned on." << std::endl;
}
};
class CarFacade {
public:
CarFacade(Engine& engine, Lights& lights)
: engine_(engine), lights_(lights) {}
void StartCar() {
engine_.Start();
lights_.TurnOn();
}
private:
Engine& engine_;
Lights& lights_;
};
class CarDriver {
public:
CarDriver(CarFacade& car) : car_(car) {}
void Drive() {
car_.StartCar();
std::cout << "Car is ready to drive." << std::endl;
}
private:
CarFacade& car_;
};
int main() {
Engine carEngine;
Lights carLights;
CarFacade car(carEngine, carLights);
CarDriver driver(car);
// The client (driver) can now use the car facade to start the car with lights on and get ready to drive.
driver.Drive();
return 0;
}
Output:
![Facade Pattern in C++](https://static.tutorialandexample.com/cpp/facade-pattern-in-cpp1.png)
Explanation:
In this example, by executing the DoSomething1() and DoSomething2() methods on the corresponding subsystem classes, the Facade class delegated the DoSomething() duty to the Subsystem1 and Subsystem2 classes.
The Client class can learn how the Subsystem1 and Subsystem2 classes are internally implemented. The Subsystem1 and Subsystem2 classes are contained within the Facade class, which also offers a streamlined interface.
The advantages of assigning tasks to subsystems:
Task delegation to subsystems has several advantages, including:
- Modularity: Delegating duties to subsystems increases the code's modularity and makes it simpler to maintain. Updates to one subsystem won't impact the other, and each subsystem can be created and tested individually.
- Reusability: Subsystems are reusable in a variety of applications. It can help developers of new applications save time and effort.
- Complexity management: Task delegation to subsystems can aid in managing complexity. Comprehending and writing code is simpler when a difficult task is divided into smaller, simpler tasks.
Common Use Cases:
The Facade Pattern is often used in many different situations and fields, such as:
- APIs: APIs frequently provide the underlying system with a Facade. Developers can utilize the API more easily due to not needing to understand the specifics of the underlying system. For instance, the Java Database Connectivity (JDBC) API provides a Facade to the underlying database system.
- Libraries: Libraries can make their functionality more accessible by using the Facade Pattern. As an illustration, the Boost C++ Libraries offer a variety of Facades for various system features.
- Libraries: Libraries can make their functionality more accessible by using the Facade Pattern. As an illustration, the Boost C++ Libraries offer a variety of Facades for various system features.
- Complex subsystems: The interaction with complex subsystems can be made simpler using the Facade Pattern. For instance, the Facade Pattern can be applied to streamline communication with a distributed network or cloud computing platform.