Skip to Content
Course content

154: The Facade Pattern

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

I see this a lot when I'm reviewing code from junior developers: they'll build a class that wraps another class and tell me they've implemented the Facade pattern. When I look closer, they've actually just built an Adapter. It's a common point of confusion because both patterns involve creating a wrapper class. But if you mistake one for the other, you'll end up over-engineering your interfaces without actually solving the problem of complexity.

"Wait, Isn't This Just an Adapter?"

The misconception is that a Facade is simply about changing an interface to make it "fit" somewhere else. If you have a LegacySystem class with a method called execute_query_v1() and you create a wrapper that calls it via a method called fetchData(), you haven't built a Facade. You've built an Adapter. You're just translating one language to another.

A Facade isn't about translation; it's about simplification. It's designed to provide a single, simple entry point to a complex subsystem of many classes. If your "Facade" only wraps one other class, you're probably just adding an unnecessary layer of redirection.

Simplifying Complexity, Not Just Changing Interfaces

Think about a home theater. To watch a movie, you don't just "press play." In reality, you have to dim the lights, lower the motorized screen, turn on the projector, set the projector to the right input, turn on the amplifier, set the volume, and finally start the Blu-ray player. Each of these is a separate device (a separate class in our code) with its own API.

Without a Facade, your client code (the person wanting to watch the movie) looks like this:


// This is a nightmare to maintain
Lights lights = new Lights();
Screen screen = new Screen();
Projector projector = new Projector();
Amp amp = new Amp();
BluRayPlayer player = new BluRayPlayer();

lights.dim(10);
screen.down();
projector.on();
projector.setInput("BluRay");
amp.on();
amp.setVolume(20);
player.play("The Matrix");

I hate seeing this in a codebase because if the Projector class changes its setInput method to require an integer ID instead of a string, you have to hunt down every single place in your app where a movie is started and fix it. It's fragile.

Instead, we build a Facade. This class doesn't replace the subsystems—it just coordinates them. The subsystems still exist and can be used individually if you really need that granular control, but for 90% of your use cases, you just want the "Easy Button."


public class HomeTheaterFacade {
    private Lights lights;
    private Screen screen;
    private Projector projector;
    private Amp amp;
    private BluRayPlayer player;

    public HomeTheaterFacade(Lights l, Screen s, Projector p, Amp a, BluRayPlayer b) {
        this.lights = l;
        this.screen = s;
        this.projector = p;
        this.amp = a;
        this.player = b;
    }

    public void watchMovie(String movie) {
        System.out.println("Get ready to relax...");
        lights.dim(10);
        screen.down();
        projector.on();
        projector.setInput("BluRay");
        amp.on();
        amp.setVolume(20);
        player.play(movie);
    }
    
    public void endMovie() {
        System.out.println("Shutting down theater...");
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        player.stop();
    }
}

Now, your client code is just theater.watchMovie("The Matrix");. You've decoupled the "intent" (watching a movie) from the "implementation" (the sequence of hardware calls). If you upgrade your projector to a model that doesn't need an input setting, you change one line in the Facade, and the rest of your application never even knows anything changed.




📋 Practical Task

Build a Unified Order Processing Facade

You are working on an e-commerce backend. Currently, to complete a purchase, the OrderService has to manually coordinate three different complex subsystems:

  • InventorySystem (methods: checkStock(itemId), reserveItem(itemId))
  • PaymentProcessor (methods: validateCard(cardNumber), charge(amount))
  • ShippingCourier (methods: calculateShipping(weight), schedulePickup(address))

Your task is to create an OrderFacade class. This class should provide a single method: placeOrder(String itemId, String cardNumber, String address, double amount).

The placeOrder method must perform these steps in order:

  1. Check if the item is in stock.
  2. If in stock, reserve the item.
  3. Validate the credit card.
  4. Charge the amount.
  5. Schedule the shipping pickup.

If any step fails (e.g., checkStock returns false), the method should return false immediately and stop the process. If all steps succeed, return true. Ensure your Facade encapsulates these three subsystems so that the main application doesn't have to deal with them individually.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.