Structural

Facade

Provide a simplified, unified interface to a complex subsystem — hide the complexity behind a single entry point.

Beginner Pattern #10 of 23

What is the Facade Pattern?

Facade provides a simple interface to a complex body of code — a library, a framework, or any complex set of classes. Clients interact with the Facade instead of the subsystem directly, reducing coupling and cognitive load.

The Facade doesn't prevent access to the subsystem — advanced users can still go direct. It simply provides a convenient, higher-level alternative for common use cases.

Real-world analogy: Starting a car. You turn a key — the Facade. Behind it: battery check, fuel injection, ignition timing, starter motor, ECU initialisation. You don't need to know any of that. The key is your Facade.

The Problem It Solves

A home theatre system has: Amplifier, DVDPlayer, Projector, Lights, Screen, PopcornPopper. Watching a movie means: turning on the popper, lowering lights, pulling down the screen, turning on the projector, setting input, turning on the amp, setting volume, starting the DVD. 8 classes, many method calls, specific order.

A HomeTheatreFacade with a single watchMovie() method hides all of this complexity. The client calls one method.

Participants

Facade
Knows which subsystem classes to delegate to and in what order. Provides simple methods that orchestrate complex subsystem interactions. Does not duplicate subsystem functionality.
Subsystem Classes
Complex classes that implement the actual functionality. Unaware of the Facade — no back-reference. Can still be used directly by advanced clients.
Client
Interacts only with the Facade for common operations. The Facade decouples the client from the subsystem's complexity.

Visual Flow Diagram

Client simple call Facade watchMovie() endMovie() listenToRadio() Amplifier DVDPlayer Projector Lights / Screen Subsystem (complex internals)

Java Code Example

Java — Home Theatre Example
// Subsystem classes — complex, many methods
public class Amplifier {
    public void on()              { System.out.println("Amp on"); }
    public void setVolume(int v) { System.out.println("Volume: " + v); }
    public void off()             { System.out.println("Amp off"); }
}
public class DVDPlayer {
    public void on()              { System.out.println("DVD on"); }
    public void play(String m)   { System.out.println("Playing: " + m); }
    public void stop()            { System.out.println("DVD stopped"); }
    public void off()             { System.out.println("DVD off"); }
}
public class Projector {
    public void on()              { System.out.println("Projector on"); }
    public void wideScreenMode() { System.out.println("Widescreen mode"); }
    public void off()             { System.out.println("Projector off"); }
}
public class TheaterLights {
    public void dim(int pct)     { System.out.println("Lights at " + pct + "%"); }
}
public class Screen {
    public void down()            { System.out.println("Screen down"); }
    public void up()              { System.out.println("Screen up"); }
}

// Facade — orchestrates the subsystem
public class HomeTheatreFacade {
    private final Amplifier     amp;
    private final DVDPlayer     dvd;
    private final Projector     projector;
    private final TheaterLights lights;
    private final Screen        screen;

    public HomeTheatreFacade(Amplifier a, DVDPlayer d,
                              Projector p, TheaterLights l, Screen s) {
        amp=a; dvd=d; projector=p; lights=l; screen=s;
    }

    // Simple interface hiding 8 subsystem calls
    public void watchMovie(String movie) {
        System.out.println("--- Get ready to watch a movie! ---");
        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        amp.on();
        amp.setVolume(5);
        dvd.on();
        dvd.play(movie);
    }

    public void endMovie() {
        System.out.println("--- Shutting down theatre ---");
        dvd.stop(); dvd.off();
        amp.off();
        projector.off();
        screen.up();
        lights.dim(100);
    }
}

// Client — one call instead of eight
public class Main {
    public static void main(String[] args) {
        HomeTheatreFacade theatre = new HomeTheatreFacade(
            new Amplifier(), new DVDPlayer(),
            new Projector(), new TheaterLights(), new Screen()
        );
        theatre.watchMovie("Inception");
        theatre.endMovie();
    }
}

When to Use / Avoid

✓ Use When

  • You want to provide a simple interface to a complex subsystem
  • There are many dependencies between clients and implementation classes
  • You want to layer your system — Facade defines the entry point to each layer
  • You need to decouple client code from a third-party library

✕ Avoid When

  • The Facade becomes a "god object" that knows too much — it should orchestrate, not implement
  • Clients need fine-grained control over the subsystem — don't force them through the Facade
  • The subsystem is simple — adding a Facade is unnecessary indirection

Real-World Examples

Pros & Cons

Pros

  • Simplifies client code — one call instead of many
  • Decouples client from subsystem — subsystem can evolve without affecting client
  • Promotes weak coupling between subsystems
  • Subsystem is still accessible directly for advanced use cases

Cons

  • Facade can become a God Object if it accumulates too much logic
  • Risk of coupling all subsystems together through the Facade
  • May hide functionality that some clients legitimately need

How Facade Can Be Broken

⚠ Attack Vectors

  • God Facade: The Facade grows to implement business logic itself rather than just orchestrating subsystems — it becomes a massive unmaintainable class
  • Bypassing the Facade: Clients reach directly into subsystem classes for convenience, creating tight coupling the Facade was meant to eliminate
  • Facade depending on client details: Facade methods take or return client-specific types rather than generic ones — couples the subsystem to the client's domain
  • Stateful Facade shared across threads: A singleton Facade that holds mutable subsystem state (e.g., current volume) is not thread-safe and causes race conditions
  • Hiding errors: Facade swallows subsystem exceptions and returns silently — client has no way to know an operation failed

✓ Prevention

  • Keep Facade thin: It orchestrates and delegates — never implements business logic. Any logic that grows in the Facade should move into the appropriate subsystem class
  • Package-protect subsystem classes: Make subsystem classes package-private so clients outside the package are forced to go through the Facade
  • Use generic types in Facade API: Facade methods should use domain-neutral types — never take UI-layer or client-specific objects as parameters
  • Keep Facade stateless or use thread-local state: Avoid mutable instance fields in a shared Facade — delegate all state to the subsystem classes
  • Always propagate exceptions: Let subsystem exceptions bubble up (or wrap in a Facade-specific exception) — never silently catch and ignore
Java — Break & Fix
// ❌ BREAKING — God Facade with business logic
public void watchMovie(String movie) {
    // ❌ Facade doing business logic, not just orchestration
    if (movie.contains("adult")) {
        verifyAge(); chargeSubscription(); logAnalytics(); // wrong place!
    }
    lights.dim(10); // ...
}

// ✅ FIX — Facade only orchestrates, logic stays in subsystem
public void watchMovie(String movie) {
    lights.dim(10); screen.down(); projector.on();
    amp.on(); dvd.on(); dvd.play(movie); // purely orchestration
}

// ❌ BREAKING — swallowing exceptions
public void watchMovie(String movie) {
    try {
        dvd.play(movie);
    } catch (Exception e) {
        // ❌ silent catch — client thinks movie is playing
    }
}

// ✅ FIX — wrap and rethrow
public void watchMovie(String movie) {
    try {
        dvd.play(movie);
    } catch (DVDException e) {
        throw new TheatreException("Could not play movie", e);
    }
}

// ❌ BREAKING — bypassing Facade directly
// client reaches into subsystem, defeats the pattern
DVDPlayer dvd = theatre.getDVD(); // ❌ leaking subsystem reference
dvd.play("raw call");

// ✅ FIX — package-private subsystem, no getters on Facade
class DVDPlayer { // package-private — only Facade in same package can use
    void play(String m) { /* ... */ }
}

How Other Patterns Relate

Interview Cheat Sheet

  1. What: A single class that wraps a complex subsystem and provides a simple interface. Clients call one method; the Facade orchestrates many subsystem calls in the right order.
  2. How: Create a Facade class that holds references to all subsystem objects. Provide high-level methods that sequence subsystem calls. Subsystem classes remain unchanged and directly accessible if needed.
  3. Key distinctions: Adapter = translates one interface. Facade = simplifies many interfaces. Mediator = coordinates two-way communication. Proxy = controls access to one object. Facade is about hiding complexity, not changing interfaces.