Skip to Content
Course content

195: The Facade Pattern in Practice

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think about the last time you sat down to watch a movie at home. If you have a decent setup, you've probably got a TV, a soundbar or receiver, a streaming box or Blu-ray player, and maybe some smart lights. To get everything ready, you could manually turn on the TV, switch it to the HDMI 2 input, power up the receiver, set the volume to 40, turn on the player, and dim the living room lights. It works, but it's a chore.

Now, imagine you have a "Movie Mode" button on a single remote. You press it once, and the remote sends the specific commands to all those different devices for you. You don't need to know the specific protocol the lights use or which input the TV needs; you just want the movie to start. That remote is a Facade.

In C++, the Facade pattern does exactly that. It provides a simplified, high-level interface to a complex set of subsystems. It doesn't replace the underlying classes—you can still access the "amplifier" or "lights" directly if you need fine-grained control—but for 90% of your use cases, the Facade makes the code much cleaner and easier to maintain.

Mapping the Remote to the Code

Let's map that analogy directly into C++. The "devices" are our subsystem classes, and the "remote" is our Facade class. I'll use a home cinema example because it perfectly illustrates the "many-to-one" relationship of this pattern.

#include <iostream>
#include <string>

// Subsystem 1: The Audio System
class Amplifier {
public:
    void on() { std::cout <"Amplifier powering up...\n"; }
    void setVolume(int level) { std::cout <"Volume set to " < level < ".\n"; }
    void off() { std::cout <"Amplifier shutting down.\n"; }
};

// Subsystem 2: The Video System
class Projector {
public:
    void on() { std::cout <"Projector warming up...\n"; }
    void setInput(std::string input) { std::cout <"Input set to " < input < ".\n"; }
    void off() { std::cout <"Projector cooling down.\n"; }
};

// Subsystem 3: The Lighting System
class SmartLights {
public:
    void dim(int level) { std::cout <"Lights dimmed to " < level < "%.\n"; }
    void brighten() { std::cout <"Lights restored to full brightness.\n"; }
};

// The Facade: The "Remote Control"
class HomeCinemaFacade {
private:
    Amplifier amp;
    Projector projector;
    SmartLights lights;

public:
    void watchMovie() {
        std::cout <"--- Preparing for Movie Mode ---\n";
        lights.dim(20);
        projector.on();
        projector.setInput("HDMI 1");
        amp.on();
        amp.setVolume(50);
        std::cout <"Enjoy your movie!\n";
    }

    void endMovie() {
        std::cout <"--- Shutting Down Cinema ---\n";
        amp.off();
        projector.off();
        lights.brighten();
    }
};

int main() {
    HomeCinemaFacade remote;
    
    // Instead of calling 6 different methods across 3 classes,
    // we just call one high-level method.
    remote.watchMovie();
    
    std::cout <"\n(Movie ends...)\n\n";
    
    remote.endMovie();
    
    return 0;
}

Why we bother with this abstraction

You might be looking at this and thinking, "I could have just written a function to do this." True, but the Facade pattern is about structural intent. By wrapping these subsystems in a class, you're creating a boundary.

I've worked on legacy projects where the "subsystems" were actually massive, terrifying third-party libraries with 50-page manuals. If you let those library calls leak into every single file of your project, you're in trouble. When the library updates and changes its API, you have to hunt through 100 files to fix it. If you use a Facade, you only have to change the code inside the Facade class. The rest of your application remains blissfully unaware that the underlying library changed.

One thing to keep in mind: don't let your Facade become a "God Object." A Facade should be a thin wrapper, not a place where you put all your business logic. Its only job is to orchestrate the subsystems. If you find yourself writing complex algorithms inside the Facade, you're probably doing it wrong; move that logic into one of the subsystems or a separate service class.




📋 Practical Task

Build a Simplified API for a File Compression Utility

You are tasked with creating a tool that handles a complex file archival process. The actual process involves three distinct steps: Scanning (finding files), Compressing (reducing size), and Encrypting (securing the file). Each of these steps is handled by its own complex class.

Your goal: Implement a BackupFacade class that provides a single method createSecureBackup(std::string folderName). This method should orchestrate the following sequence:

  1. Call a method in the FileScanner class to list files.
  2. Call a method in the Compressor class to zip those files.
  3. Call a method in the Encryptor class to encrypt the resulting zip file.

Make sure that the main()` function only interacts with the BackupFacade, never with the scanner, compressor, or encryptor directly. This ensures that if you ever decide to change your encryption algorithm or compression library, you only have to modify the Facade.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.