Facade Pattern


  • Description: Provide a single, simplified interface to a complicated subsystem of classes — clients depend on the facade, not the subsystem's internals.
  • My Notion Note ID: K2C-2-10
  • Created: 2026-05-22
  • Updated: 2026-05-22
  • License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io

Table of Contents


1. Intent

  • Subsystem with many cooperating classes → clients drown in setup, ordering, lifetimes.
  • Facade exposes one coherent API for the common 80% use case. Power users still reach into the subsystem directly.
  • Decouples client from subsystem internals → swap implementation, refactor internals, no client breakage.

Goal is simplification, not isolation. Facade is not a wall — clients can bypass it.

Canonical examples → ffmpeg lib wrappers, OpenGL framework "scene" classes, HTTP client SDKs that hide socket / DNS / TLS / parsing.


2. Structure

Role Responsibility
Facade Single entry point. Translates high-level calls into orchestrated subsystem calls.
Subsystem classes Do the real work. Unaware of the Facade.
Client Talks to the Facade (default) or directly to subsystem classes (advanced).

No abstract base needed. Facade is usually a concrete class with a small, task-oriented API.


3. C++ Example

3.1 Video conversion facade

#include <string>
#include <iostream>
#include <utility>

// Subsystem — verbose, granular
class VideoFile { public: explicit VideoFile(std::string p) : path_(std::move(p)) {} std::string path() const { return path_; } private: std::string path_; };
class CodecFactory { public: std::string pick(const VideoFile&) { return "mpeg4"; } };
class BitrateReader { public: std::string read(const VideoFile&, const std::string&) { return "<bytes>"; } std::string convert(const std::string& buf, const std::string&) { return buf + "/converted"; } };
class AudioMixer { public: std::string fix(const std::string& buf) { return buf + "+audio"; } };

// Facade — single call wrapping the whole pipeline
class VideoConverter {
public:
    std::string convert(const std::string& path, const std::string& target) {
        VideoFile file{path};
        auto src_codec = CodecFactory{}.pick(file);
        BitrateReader reader;
        auto buf = reader.read(file, src_codec);
        auto converted = reader.convert(buf, target);
        return AudioMixer{}.fix(converted);
    }
};

int main() {
    VideoConverter conv;
    std::cout << conv.convert("input.mp4", "ogg") << "\n";
}
  • Client sees one method. Internals can swap codec libs, add caching, change order.
  • Power-user code still constructs BitrateReader directly if needed.

3.2 Library facade over a C API

// Subsystem: bare C handle-based API (sqlite3, libcurl, ffmpeg)
// Facade: RAII C++ wrapper exposing only a few high-level operations.

class Db {
public:
    explicit Db(const std::string& path);   // calls sqlite3_open, checks, throws
    ~Db();                                  // sqlite3_close
    Db(const Db&) = delete;
    Db& operator=(const Db&) = delete;
    Db(Db&&) noexcept;
    Db& operator=(Db&&) noexcept;

    std::vector<Row> query(const std::string& sql);
    void exec(const std::string& sql);
private:
    struct sqlite3* h_;                     // raw handle hidden
};
  • Hides handle management, error code translation, statement prep/finalize.
  • Common case: db.query("SELECT ..."). Power user can still get h_ via a friend / accessor for esoteric calls.

4. When to Use

Use when:

  • Subsystem has high learning curve / many classes / fragile init order.
  • Most clients only need a small slice of functionality.
  • Want a stable API in front of churn-prone internals.
  • Layering — Facade at each layer boundary keeps layers loosely coupled.

Avoid when:

  • Subsystem is already simple → Facade adds a useless indirection.
  • Facade keeps growing endpoints → it's becoming a god-object; split.
  • Need full subsystem flexibility → don't force everyone through a narrow door.

5. Variants and Pitfalls

  • Multiple facades for the same subsystem, each targeting a use case (read-only client, admin client). Better than one fat facade.
  • Facade + Singleton. Convenient but couples global state; testing pain. Prefer injecting the facade.
  • Abstract Facade. Pure-virtual facade base + concrete implementations → swappable backends. Crosses into Strategy / Bridge territory.
  • Facade as service entry. Microservice public API is a Facade over internal services.

Pitfalls:

  • God-facade. One class exposing 50 methods spanning unrelated concerns → split by use case.
  • Leaky facade. Returns subsystem types (raw handles, internal enums) → clients couple to internals anyway.
  • Facade-only access. If clients are forbidden to touch the subsystem, that's closer to Mediator (centralized control). Facade allows bypass; Mediator does not.
  • Hidden cost. A simple facade call may do expensive setup → document complexity, don't surprise the caller.
  • Versioning. Once published, breaking the facade breaks every caller. Add new methods; deprecate, don't remove.

6. Related Patterns

  • Facade vs Adapter. Adapter converts one interface to another expected interface — 1-to-1 shape translation. Facade defines a new interface over a subsystem — 1-to-many. Adapter solves a fit problem; Facade solves a complexity problem.
  • Facade vs Mediator. Both centralize. Mediator owns the interaction protocol — colleagues talk only through the mediator, never directly. Facade is a convenience — subsystem classes still talk to each other directly; clients optionally route through the facade. Mediator restricts; Facade simplifies.
  • Facade vs Decorator. Decorator preserves the wrapped interface and adds behavior. Facade introduces a new, smaller interface over many classes.
  • Facade vs Proxy. Proxy keeps the subject's interface and controls access. Facade defines a new, smaller interface over many subjects.
  • Facade + Abstract Factory. Facade often hands clients pre-configured subsystem objects → the Facade's constructor or factory method plays Abstract Factory.
  • Singleton. Facades are often singletons, but that's a separate decision; nothing in Facade requires global state.

7. References

  • GoF — Design Patterns, Facade ch.
  • refactoring.guru — Facade
  • sourcemaking — Facade
  • Larman — Applying UML and Patterns, layered architecture