← All Patterns
structural4 participants

Facade

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

The Problem

Using a complex subsystem directly requires knowing dozens of classes, their initialization order, and their interdependencies. Client code becomes tightly coupled to implementation details. Every new caller must repeat the same orchestration boilerplate.

Structure

Clientrun()one callSimEngine«Facade»run(events)orchestrates all belowEventSchedschedule()BarrierSyncsync(n)Profilerreport()Client calls one method — Facade orchestrates 3+ subsystems internally

Execution Walkthrough

1

Complex subsystem

EventScheduler, BarrierSync, Profiler, ResultWriter — each has its own API and must be used in the right order.

2

Facade owns them

3

One entry point

4

Orchestration

5

Client simplicity

Code Comparison

Every caller must know the correct initialization and call order. One wrong step corrupts results.

// BAD: client orchestrates everything manually
void run_simulation(std::span<Event> events, int threads) {
  EventScheduler scheduler;
  BarrierSync    barrier;
  Profiler       profiler;
  ResultWriter   writer;

  profiler.start();                    // must be first
  for (auto& e : events)
    scheduler.schedule(e);             // then schedule
  barrier.sync(threads);               // then sync
  auto report = profiler.report();     // then collect
  writer.write(report);                // then write
  // Every caller copy-pastes this block.
  // Get order wrong? Silent bad results.
}

Full C++ Implementation

C++ · Facade
#include <iostream>
#include <vector>
#include <span>
#include <string>

struct Event { int id; double time; };

struct EventScheduler {
  int count_ = 0;
  void schedule(Event e) { ++count_; }
  int count() const { return count_; }
};

struct BarrierSync {
  void sync(int threads) {
    std::cout << "[barrier] syncing " << threads << " threads\n";
  }
};

struct Profiler {
  int events_ = 0;
  void start() {}
  void record(int n) { events_ = n; }
  std::string report() {
    return "events=" + std::to_string(events_);
  }
};

struct ResultWriter {
  void write(const std::string& r) {
    std::cout << "[result] " << r << "\n";
  }
};

class SimulationEngine {
  EventScheduler scheduler_;
  BarrierSync    barrier_;
  Profiler       profiler_;
  ResultWriter   writer_;
  int            threads_;
public:
  explicit SimulationEngine(int t) : threads_(t) {}

  void run(std::span<Event> events) {
    profiler_.start();
    for (auto& e : events) scheduler_.schedule(e);
    barrier_.sync(threads_);
    profiler_.record(scheduler_.count());
    writer_.write(profiler_.report());
  }
};

int main() {
  std::vector<Event> events{{1,0.1},{2,0.2},{3,0.3}};
  SimulationEngine engine{4};
  engine.run(events);
}

Participants

FacadeSubsystemASubsystemBSubsystemC

Where it is used

Compiler front-ends

clang::CompilerInstance drives lexer, parser, sema, codegen, and diagnostics via one object.

OpenGL / Vulkan

GLFW/GLEW hide 50+ platform-specific calls needed to open a window and get a rendering context.

OS system calls

POSIX stdio (fopen/fread/fclose) is a Facade over open/read/close/lseek with hidden buffering.

Simulation engines

SimEngine::run_all() drives event scheduling, barrier sync, profiler, and serialization behind one call.

Adapter