Facade

Design patterns

  |   7 min read

Purpose

Facade (structural pattern) unifies and simplifies access to a complex system or part of it by providing an organized programming interface. This pattern takes inspiration from architecture - the facade, which is the main and often spectacular facade of the building and plays a representative role. This pattern analogically acts as an “overlay” by adding another higher level of abstraction. Therefore, it does not extend the functionality of the system, but makes the use of its functions simpler and the code more readable. Facade works wherever performing operations in a system entails sequences of operations performed in different parts of the system in a specific order. It is a frequently used pattern and is an inseparable element of many refactoring. A programmer using Facades does not need to know how exactly how given operation works, however it does not release him from reading the attached documentation.

Limitations

Facade can store references to objects, so there is a strong temptation that unit operations can also be available to the programmer. However this approach, contradicts the idea of this pattern that hides implementation details. When designing facades, it is necessary to analyze which unit operations from its objects should be available in the interface and which ones should be hidden. In such a way that the change in the state of the objects does not affect the proper works of Facade.

Usage

It can be said that every API or framework is Facade. It acts as an mediator between a complicated system and a client, providing access to simple but often complex “in the middle” functions. The ‘Facade’ is therefore almost everywhere.

Implementation

Creating Facade is mainly about aggregating related objects and providing public methods that implement specific execution of the requested operation.

Facade diagram

The following listing shows an example of the implementation of Facade operating on two types of objects, the Utils and Enum classes.

public class Facade {

    private Element1 element1;
    private Element2 element2;

    public Facade() {
        element1 = new Element1();
        element2 = new Element2();
    }

    public void operation1(Action action) {
        if(action == Action.INCREASE)
            element1.increase();
        else if(action == Action.DECREASE)
            element1.decrease();
        else if(action == Action.DOUBLE)
            element1.makeDouble();
        else
            element2.restart();
    }

    public void operation2() {
    	if(Utils.isAccessProvided()) {
            element2.startAction(element1.getState());
            element1.refreshState();
        }
        else
            Utils.printError();
    }
}

Example

The application is a mockup of a music player (Client) who uses own MediaPlayer (Facade). The programmer creating the player’s applications has to prepare a comprehensible interface that allows another programmer to simply and briefly call up the basic functions of the player such as play, pause, stop, changing songs or adjusting the volume (Facade methods). Knowledge about the way the API is working is not necessary for the programmer to implement the basic functions of the player. In the case of ambiguity or advanced operations, he should refer to the documentation and the source code. Properly programmed MediaPlayer communicates with various other system elements and external modules, delegates them individual tasks and manages the entire process. Each time, manually creating the basic functions of the player can be time-consuming, unclear and go beyond the possibilities of a novice programmer, so the use of Facade is highly desirable here. It provides the system functions in a clear way, hiding the mechanism of action. The following listing shows the facade of MediaPlayer.

public class MediaPlayer {

    private FileManager fileManager;
    private HardwarePlayer hardwarePlayer;
    private SongFile songFile;
    private List<SongFile> songs;
    private int currentSong;
    private int currentVolume;
    private boolean isPlaying;

    public MediaPlayer(Source hardwareSource, boolean isSdCard) {
        fileManager = new FileManager(isSdCard);
        hardwarePlayer = new HardwarePlayer(hardwareSource);
        songs = new ArrayList<>();
        currentSong = 0;
        currentVolume = 5;
        isPlaying = false;
        createPlaylist();
    }

    public void play() {
        loadSong();
        hardwarePlayer.play();
        isPlaying = true;
    }

    public void pause() {
        hardwarePlayer.pause();
        isPlaying = false;
    }

    public void stop() {
        hardwarePlayer.stop();
        currentSong = 0;
        isPlaying = false;
    }

    public void increaseVolume() {
        if(currentVolume < 10)
            currentVolume++;
        changeVolume();
    }

    public void decreaseVolume() {
        if(currentVolume > 0)
            currentVolume--;
        changeVolume();
    }

    public void nextSong() {
        if(currentSong == songs.size() + 1)
            currentSong = 0;
        else
            currentSong++;
        changeSong();
    }

    public void previousSong() {
        if(currentSong == 0)
            currentSong = songs.size() - 1;
        else
            currentSong--;
        changeSong();
    }

    public void loadSong() {
        SongBytes songBytes = Converter.convertToBytes(songFile);
        hardwarePlayer.load(songBytes);
    }

    public void createPlaylist() {
        List<File> files = fileManager.readSongsList();
        songs = Converter.convertToSongFiles(files);
    }

    public boolean isPlaying() {
        return isPlaying;
    }

    private void changeVolume() {
        hardwarePlayer.pause();
        hardwarePlayer.setVolume(currentVolume);
        hardwarePlayer.start();
    }

    private void changeSong() {
        songFile = songs.get(currentSong);
        hardwarePlayer.stop();
        play();
    }

    //other methods
}

‘Facade’ prepared in this way with working modules, allows to easily connect actions to the user interface, which also affects to the readability of the code.

Libraries

The implementation of Facade is as different as the implementation of a given problem. Therefore there is no single rule for its creation, and there is no reason to exist libraries that streamline the process of implementing Facade. Each external library (eg: Retrofit, Glide) is Facade in the software being created.