Files
Design-Patterns/docs/facade.md
2026-08-13 22:47:17 -04:00

6.6 KiB

← Design patterns index

Facade

Category: Structural | Source: patterns/facade/CarEngineFacade/ (+ CarEngineFacadeRecording/)

Problem

Using a complex subsystem directly means the client must know every class, instantiate them all, and call their methods in the correct order. That sequencing knowledge leaks into — and couples — every piece of business logic that touches the subsystem.

Solution

Add one class that owns the wiring and the call sequence, exposing a couple of high-level methods. The subsystem classes stay completely unaware the facade exists. A facade adds no new functionality; it only hides complexity, deliberately trading away some of the subsystem's generality for ease of use.

Participants (GoF names → this repo)

  • Facade → Facade — knows which subsystem class handles what, and in what order.
  • Subsystem classes → AirFlowController, FuelInjector, Starter, CoolingController, CatalyticConverter — do the work, hold no reference to the facade.
classDiagram
    class Facade {
        -AirFlowController _air
        -FuelInjector _fuel
        -Starter _start
        -CoolingController _cool
        -CatalyticConverter _cat
        +Start()
        +Stop()
    }
    Program --> Facade : only dependency
    Facade --> AirFlowController
    Facade --> FuelInjector
    Facade --> Starter
    Facade --> CoolingController
    Facade --> CatalyticConverter

Note every arrow points away from the facade. Nothing points back — that one-directionality is what distinguishes Facade from Mediator.

Code

// The five subsystem classes — real project has empty "// implementation" bodies
public class AirFlowController  { public void TakeAir() {}  public void Off() {} }
public class FuelInjector       { public void On() {} public void Inject() {} public void Off() {} }
public class Starter            { public void Start() {} }
public class CoolingController  { public void Run() {} public void Stop() {} public void Cool() {} }
public class CatalyticConverter { public void On() {} public void Off() {} }
// Facade.cs — owns construction AND ordering
public class Facade
{
    private readonly AirFlowController _air = new();
    private readonly CatalyticConverter _cat = new();
    private readonly CoolingController _cool = new();
    private readonly FuelInjector _fuel = new();
    private readonly Starter _start = new();

    public void Start()
    {
        _air.TakeAir();     // order matters: air before fuel before spark
        _fuel.On();
        _fuel.Inject();
        _start.Start();
        _cool.Run();
        _cat.On();
    }

    public void Stop()
    {
        _fuel.Off();        // NOT the reverse of Start() — shutdown has its own order
        _cat.Off();
        _cool.Cool();       // keep cooling after fuel is cut, then stop cooling
        _cool.Stop();
        _air.Off();
    }
}
// Program.cs — the file keeps the "before" version commented out for comparison

// WITHOUT the facade — client owns construction AND sequencing:
//   var airflow = new AirFlowController();
//   var fuel = new FuelInjector();
//   var start = new Starter();
//   var cool = new CoolingController();
//   var cat = new CatalyticConverter();
//   airflow.TakeAir(); fuel.On(); fuel.Inject(); start.Start(); cool.Run(); cat.On();
//   fuel.Off(); cat.Off(); cool.Cool(); cool.Stop(); airflow.Off();

// WITH the facade — 11 ordered calls across 5 types collapse to 2 calls on 1 type:
var facade = new Facade();
facade.Start();
facade.Stop();

That the Stop() sequence is not Start() reversed is the point of the whole example: the ordering is real domain knowledge, it is easy to get wrong, and it should live in exactly one place.

Output (verified)

None — the subsystem method bodies are deliberately empty stubs, so the program runs and exits silently. This example is about the shape of the dependencies, not runtime behavior.

GoF pseudocode (video conversion library)

class VideoConverter is
  method convert(filename, format): File is
     file = new VideoFile(filename)
     sourceCodec = new CodecFactory.extract(file)
     if (format == "mp4") destinationCodec = new MPEG4CompressionCodec()
     else destinationCodec = new OggCompressionCodec()
     buffer = BitrateReader.read(filename, sourceCodec)
     result = BitrateReader.convert(buffer, destinationCodec)
     result = (new AudioMixer()).fix(result)
     return new File(result)

convertor = new VideoConverter()
mp4 = convertor.convert("youtubevideo.ogg", "mp4")

Applicability

  • You want a simple, limited interface to a complex subsystem that satisfies most clients; the few needing more can still reach past the facade.
  • There are many dependencies between clients and a subsystem's implementation classes.
  • You want to layer subsystems — give each layer a facade and let layers talk only through them.

Pros / Cons

Pros Cons
Isolates client code from subsystem complexity Can grow into a god object coupled to everything
Reduces the number of objects clients deal with; cuts compile-time dependencies Adds an indirection layer that must be maintained
Doesn't prevent direct subsystem access when a client needs it A facade that grows too large needs splitting into refined facades

Seen in the wild (.NET)

  • HttpClient over the sockets/handler/header stack.
  • File.ReadAllText over FileStream + StreamReader + encoding detection.
  • WebApplication.CreateBuilder over the hosting/DI/config subsystems.
  • Adapter makes an existing interface usable; Facade defines a new, simpler one. Adapter usually wraps one object, Facade a whole subsystem.
  • Facade vs. Mediator — both organize collaboration among many classes. Facade is one-directional and the subsystem doesn't know it exists; Mediator is multi-directional and colleagues explicitly depend on it.
  • Abstract Factory is the alternative when you only want to hide subsystem object creation.
  • A facade is often made a Singleton, since one instance usually suffices.

← Prev: Decorator · All patterns · Next → Mediator