The Facade Pattern is a structural design pattern that provides a unified high-level interface to a set of interfaces in a subsystem, making the subsystem easier to use. This pattern simplifies the interaction between the client and the subsystem by introducing a facade role, allowing the client to interact only with the facade without needing to understand the complex structure of the subsystem.
Core Roles of the Facade Pattern
- Facade: Provides a unified interface that encapsulates the complexity of the subsystem.
- Subsystem: Composed of multiple related classes that implement specific functionalities.
- Client: Interacts with the subsystem through the facade interface without needing to understand the subsystem’s details.
Example Implementation of the Facade Pattern
Below is an example demonstrating the implementation of the facade pattern using a “Home Theater System”. The home theater includes multiple devices such as a projector, sound system, and DVD player, and the facade pattern can simplify the coordination of these devices:
#include <iostream>
#include <string>
// Subsystem: Projector
class Projector {
public:
void on() {
std::cout << "Projector is ON" << std::endl;
}
void off() {
std::cout << "Projector is OFF" << std::endl;
}
void setInput(const std::string& source) {
std::cout << "Projector input set to: " << source << std::endl;
}
void setResolution(const std::string& resolution) {
std::cout << "Projector resolution set to: " << resolution << std::endl;
}
};
// Subsystem: Sound System
class SoundSystem {
public:
void on() {
std::cout << "Sound system is ON" << std::endl;
}
void off() {
std::cout << "Sound system is OFF" << std::endl;
}
void setVolume(int level) {
std::cout << "Sound volume set to: " << level << std::endl;
}
void setSurroundSound(bool enable) {
if (enable) {
std::cout << "Surround sound is ON" << std::endl;
} else {
std::cout << "Surround sound is OFF" << std::endl;
}
}
};
// Subsystem: DVD Player
class DvdPlayer {
private:
std::string currentMovie;
public:
void on() {
std::cout << "DVD player is ON" << std::endl;
}
void off() {
std::cout << "DVD player is OFF" << std::endl;
}
void play(const std::string& movie) {
currentMovie = movie;
std::cout << "Playing movie: " << currentMovie << std::endl;
}
void pause() {
std::cout << "Movie " << currentMovie << " is paused" << std::endl;
}
void stop() {
std::cout << "Movie " << currentMovie << " is stopped" << std::endl;
}
};
// Facade Class: Home Theater Facade
class HomeTheaterFacade {
private:
Projector* projector;
SoundSystem* soundSystem;
DvdPlayer* dvdPlayer;
public:
// Initialize all subsystems
HomeTheaterFacade(Projector* p, SoundSystem* s, DvdPlayer* d)
: projector(p), soundSystem(s), dvdPlayer(d) {}
// Simplified interface: Prepare to watch a movie
void watchMovie(const std::string& movie) {
std::cout << "\n=== Preparing to watch movie ===" << std::endl;
projector->on();
projector->setInput("DVD");
projector->setResolution("4K");
soundSystem->on();
soundSystem->setVolume(8);
soundSystem->setSurroundSound(true);
dvdPlayer->on();
dvdPlayer->play(movie);
}
// Simplified interface: Pause the movie
void pauseMovie() {
std::cout << "\n=== Pausing movie ===" << std::endl;
dvdPlayer->pause();
}
// Simplified interface: End the movie
void endMovie() {
std::cout << "\n=== Ending movie ===" << std::endl;
dvdPlayer->stop();
dvdPlayer->off();
soundSystem->off();
projector->off();
}
};
// Client usage
int main() {
// Create subsystem objects
Projector* projector = new Projector();
SoundSystem* soundSystem = new SoundSystem();
DvdPlayer* dvdPlayer = new DvdPlayer();
// Create facade object
HomeTheaterFacade* homeTheater = new HomeTheaterFacade(projector, soundSystem, dvdPlayer);
// Client operates the complex subsystem through the facade interface
homeTheater->watchMovie("Interstellar");
homeTheater->pauseMovie();
homeTheater->watchMovie("Interstellar"); // Continue playing
homeTheater->endMovie();
// Clean up resources
delete homeTheater;
delete dvdPlayer;
delete soundSystem;
delete projector;
return 0;
}
How the Facade Pattern Works
- The facade class encapsulates the interaction details of multiple subsystems, providing a clean high-level interface.
- The client only needs to call the methods of the facade class without directly interacting with the subsystems.
- The facade class is responsible for coordinating the order of calls and interactions between multiple subsystems.
- Subsystems can interact with each other, but the client does not need to understand these details.
Difference Between Facade Pattern and Mediator Pattern
- Facade Pattern: Focuses on simplifying the interaction between the client and the subsystems, allowing direct communication between subsystems.
- Mediator Pattern: Focuses on communication between subsystems, where subsystems communicate indirectly through a mediator.
Application Scenarios of the Facade Pattern
- When there is a need to simplify the use of a complex subsystem.
- When a unified interface is needed for a complex subsystem.
- When there is a need to isolate the client from the subsystem, reducing their dependencies.
- In layered architectures, a facade interface can be provided for each layer to simplify inter-layer interactions.
Advantages and Disadvantages of the Facade Pattern
Advantages:
- Simplifies the interaction between the client and the subsystems, reducing the classes and methods the client needs to understand.
- Reduces the coupling between the client and the subsystems, improving system maintainability.
- Facilitates the extension and reuse of subsystems.
- Multiple facade classes can exist, each providing interfaces for different scenarios.
Disadvantages:
- The facade class may become too large, taking on too many responsibilities, violating the single responsibility principle.
- Adding new subsystem functionality may require modifying the facade class, violating the open-closed principle.
- May hide the flexibility of the subsystems, limiting advanced users’ customization of the system.
The facade pattern is also reflected in the C++ standard library, for example, <span>std::iostream</span> serves as a facade for lower-level stream classes like <span>std::istream</span> and <span>std::ostream</span>, providing a simple and unified interface for complex input and output operations. The facade pattern is widely used in large frameworks to provide a clean API entry point.