Facade Design Pattern

In OOPS, Facade is a structural design pattern that simplifies the interaction between the client and complex subsystem. For this, we design a facade class that works as an interface between the client and interdependent classes in the subsystem. In other words, the facade is a wrapper class, which encapsulates the complexity of the subsystem and provides a simple interface to the client. So the client will interact only with the facade object, which interacts with the subsystem objects to perform the requested operations.

Let’s understand via an analogy

Suppose we are at a restaurant and want to order a meal. If we go directly to the kitchen, we need to communicate with various chefs. This can be a complicated process. One simple solution would be to interact with a simplified interface i.e. Waiter. Here waiter will act as a facade for the kitchen system. So we will tell the waiter what we want, and waiter will communicate order to the kitchen. Now kitchen will prepare the meal and sends it through waiter. 

In simple words: We don’t need to worry about the complexity of the kitchen, such as which chef to talk to, how to communicate the order, or how the meal is prepared. We only need to interact with the waiter.

Some critical ideas to think!

  • Food-order of client would require a series of carefully sequenced method calls of two different subsystems (Waiter and Kitchen).
  • Sometimes subsystem task is dependent on the another task. For example, the kitchen cannot prepare the food if the waiter doesn’t bring the order to the kitchen. The waiter cannot serve the customer if the food is not cooked.

High level visualisation of facade design pattern

Let’s understand via example

Suppose we have a system that manages various operations related to a car like starting the engine, changing gears, and turning the car. This system consists of multiple interfaces, classes, and methods, which can be complicated for a client to use directly.

interface Engine {
    void start();
    void stop();
}

interface Gearbox {
    void shiftUp();
    void shiftDown();
}

interface Steering {
    void turnLeft();
    void turnRight();
}

To solve this problem, we will create a Facade interface i.e. a unified interface for the above set of interfaces. In other words, we will wrap the car subsystem with a simpler CarFacade interface. Here is an example:

interface CarFacade {
    void start();
    void stop();
    void shiftUp();
    void shiftDown();
    void turnLeft();
    void turnRight();
}

This interface will be more accessible and easier for a client to use. Now we will define a concrete implementation CarFacadeImpl class that implements the CarFacade interface, which internally uses the car subsystem to perform the required tasks.

Java code implementation of CarFacadeImpl

class CarFacadeImpl implements CarFacade {
    private Engine engine;
    private Gearbox gearbox;
    private Steering steering;

    public CarFacadeImpl() {
        engine = new EngineImpl();
        gearbox = new GearboxImpl();
        steering = new SteeringImpl();
    }

    public void start() {
        engine.start();
    }

    public void stop() {
        engine.stop();
    }

    public void shiftUp() {
        gearbox.shiftUp();
    }

    public void shiftDown() {
        gearbox.shiftDown();
    }

    public void turnLeft() {
        steering.turnLeft();
    }

    public void turnRight() {
        steering.turnRight();
    }
}

Here CarFacadeImpl has references to the Engine, Gearbox, and Steering interfaces, which uses them to implement the methods of the CarFacade interface. Now the client will use the CarFacade interface to perform various car operations, without worrying about the subsystem complexity. Here’s an example of how a client can use the CarFacade:

public class Client {
    public static void main(String[] args) {
        CarFacade car = new CarFacadeImpl();
        car.start();
        car.shiftUp();
        car.turnLeft();
        car.shiftDown();
        car.turnRight();
        car.stop();
    }
}

Facade Pattern decouples client code from the implementation of the subsystem and helps us to modify the subsystem without affecting the client code. But there few drawbacks which we need to take care:

  • Sometimes, facade can become a god object coupled to all classes of the application. To avoid this, it's important to design the facade to have a clear and focused responsibility.
  • If the facade is the only access point for the subsystem, it may limit the flexibility and features available to advanced users who need more fine-grained control over the subsystem. In such cases, we need to provide additional interfaces or access points to cater to the needs of different user groups. This is like a trade-off between simplicity and flexibility.

Steps to implement Facade pattern

Step 1: Check whether it is possible to provide a simpler interface than what the existing subsystem already provides to make the client code independent from many subsystem’s classes. If yes, then move forward to design a unified Facade interface for the subsystem.

Step 2: We need to design a Facade class that implements the Facade interface and encapsulates the subsystem. This class should capture the collaborations and functionality of the subsystem and redirect the calls from the client code to the appropriate objects of the subsystem.

Step 3: If the Facade becomes too big, we can think to extract part of its behavior to additional Facade classes. Designing additional Facades can help in providing flexibility and handling various types of clients.

Step 4: Clients can now use the Facade interface to instantiate the desired Facade object based on their needs. Here client is only coupled to the Facade interface and protected from any changes in the subsystem code. When a subsystem gets upgraded to a new version, you will only need to modify the code in the facade.

Overall, facade pattern is a trade-off between simplification and restriction. Over-simplifying a system can mean that the user is over-restricted, leading to less freedom than necessary. Under-simplifying the Facade pattern can result in too much freedom, making the Facade pattern irrelevant. Finding the right balance is what makes a good, useful, and effective Facade.

Let’s take another example!

Suppose we have a music player system that has various components like an audio player, equalizer, playlist manager, and visualizer. Here each component has a set of methods that can be used to control its behaviour.

Without using the Facade pattern, the client should know the details of each of these components to use them. On another side, by using the Facade pattern, we can provide a simple interface that encapsulates the complexity of the music player system.

interface MusicPlayerFacade {
    void play();
    void pause();
    void stop();
    void nextTrack();
    void prevTrack();
    void setVolume(int volume);
}

class MusicPlayerFacadeImpl implements MusicPlayerFacade {
    private AudioPlayer audioPlayer;
    private Equalizer equalizer;
    private PlaylistManager playlistManager;
    private Visualizer visualizer;

    public MusicPlayerFacadeImpl() {
        audioPlayer = new AudioPlayer();
        equalizer = new Equalizer();
        playlistManager = new PlaylistManager();
        visualizer = new Visualizer();
    }

    public void play() {
        audioPlayer.play();
        visualizer.showVisuals();
    }

    public void pause() {
        audioPlayer.pause();
        visualizer.hideVisuals();
    }

    public void stop() {
        audioPlayer.stop();
        visualizer.hideVisuals();
    }

    public void nextTrack() {
        audioPlayer.playNextTrack();
        visualizer.showVisuals();
        playlistManager.nextTrack();
    }

    public void prevTrack() {
        audioPlayer.playPrevTrack();
        visualizer.showVisuals();
        playlistManager.prevTrack();
    }

    public void setVolume(int volume) {
        audioPlayer.setVolume(volume);
    }
}

In the above example, the MusicPlayerFacade interface defines a simple set of methods that clients can use. The MusicPlayerFacadeImpl class implements this interface and provides a simple interface to control the system of the music player.

One important thing to note here: Sometimes Facade object does not provide all the features of the underlying subsystem. For example, advanced users may need to access the equalizer directly in order to fine-tune the audio settings. However, for most users, the simple interface provided by the Facade is sufficient.

Key points to remember

  • Facade is beneficial when we only want to include features that the user needs or require only a small portion of the functionality that a complex subsystem offers.
  • Facade deals with interfaces, not implementation. Its purpose is to hide the internal complexity behind a single interface that appears simple on the outside. Here Facade object knows where to direct the client’s request and how to operate all the moving parts.
  • We can create more than one Facade to prevent polluting a single Facade with unrelated features. These additional Facades can be used by both clients and other Facades.
  • Complex subsystem may contain various objects, and to make them all do something meaningful: We need to understand the implementation details, initialize objects in the correct order, and provide them with data in the proper format. Here subsystem classes are not aware of the existence of the Facade and operate within the system.

Facade vs. Adapter Pattern

  • Most of the time, Facade wraps multiple objects, while Adapter wraps a single object. However, it is also possible for the Facade to provide a simplified interface to a single complex object, and for the Adapter to wrap several legacy objects.
  • While both the Adapter and Facade are used as wrappers, they serve different purposes. The intent of the Facade is to simplify the interface of a subsystem, while the Adapter pattern uses an existing interface to make two incompatible systems work together.

Facade vs. Mediator Pattern

Mediator and Facade patterns have similarities: both abstract the functionality of existing classes and aim to organize collaboration between tightly coupled classes. There are few differences:

  • Mediator centralizes communication between objects and adds value to the system by enabling collaboration between related objects. In other words, Mediator is responsible for centralising communication between the components of the system. The components only know about the mediator object and don’t communicate directly with each other.
  • Facade defines a simplified interface to a subsystem of objects, but it doesn’t introduce any new functionality. The subsystem itself is unaware of the facade, and objects within the subsystem can communicate directly.

How Facade is related with other design patterns?

  • We often implement Facade objects as Singletons because a single facade object is usually sufficient.
  • Abstract Factory can serve as an alternative to Facade when we only want to hide the way the subsystem objects are created (or hide platform-specific classes) from the client code.
  • Flyweight shows how to make lots of little objects, whereas Facade shows how to make a single object that represents an entire subsystem.
  • While Facade is similar to Proxy in that both buffer a complex entity and initialize it on their own, Proxy has the same interface as its service object, making them interchangeable, which is not the case for Facade.

Let’s understand the use case of Facade

Suppose we want to integrate our code with a library that contains various objects. For this, we need to initialize all these objects, track their dependencies, and ensure that methods are executed in the correct order. Due to this, our code becomes tightly coupled with the implementation details of the third-party classes, which makes it challenging to understand and maintain. What should we do to solve this problem?

One approach is to use the facade class, which acts as a mediator between the code and the library, hide the implementation details and provide an interface that the code can use. In other words, we can create a new class that exposes a simplified API for the objects and methods of the library.

This class will be responsible for initializing and managing the library objects, tracking their dependencies, and ensuring that methods are executed in the correct order. Now, if we ever need to switch to a different library, we only need to update the implementation of the facade class, rather than our entire codebase.

Some other real-life applications of Facade

  • Web development frameworks use Facade to simplify the process of building web applications. Here Facade provides a unified interface for the user interface, web server, databases and other components.
  • Payment gateways use Facade to provide a consistent interface for payment processing. Here Facade hides the complexity of the payment process, which makes it easy to integrate with the gateway.
  • Operating systems use Facade to provide a unified interface to the underlying hardware and software components.
  • Graphics software uses Facade to provide a simplified interface to components like an image editor, video editor, audio editor, etc.

We highly recommend to explore some other code examples and applications of the facade pattern. If you have any queries/doubts/feedback, please write us at contact@enjoyalgorithms.com. Enjoy learning, Enjoy OOPS, Enjoy algorithms!

More from EnjoyAlgorithms

Self-paced Courses and Blogs