Façade - Programming in the Large with Design Patterns (2012)

Programming in the Large with Design Patterns (2012)

Chapter 10. Façade

Introduction

If you believe the old adage “There's no problem in Computer Science that can't be solved by adding another layer of abstraction to it.” you are going to like the Façade design pattern. The Façade design pattern adds another layer of abstraction to a complex or poorly designed subsystem.

A good example of the Façade design pattern is the JOptionPane class in the Java Class Library. JOptionPane simplifies and unifies the low level interface for creating dialog boxes in Java.

To appreciate the benefits of using JOptionPane to create dialog boxes, consider the amount of code needed to create and show a simple dialog box using the base Swing classes in the Java Class Library:

JPanel panel = new JPanel();

JLabel messageLabel =

new JLabel("Press OK to continue.");

JButton OKButton = new JButton("OK");

final JDialog customDialog = new JDialog();

panel.add(messageLabel);

panel.add(OKButton);

customDialog.getContentPane().add(

panel,BorderLayout.CENTER);

customDialog.pack();

OKButton.addActionListener(new ActionListener(){

public void actionPerformed(ActionEvent e){

customDialog.dispose();

}

});

customDialog.setVisible(true);

For all the effort, the result is a rather plain looking dialog box:

Compare the code above to what is required when using JOptionPane to create and show a similar dialog box:

JOptionPane.showMessageDialog(

null,"Press OK to continue.");

It takes just one line of code to create a dialog box with JOptionPane. The code is simple and the resulting dialog box has a standard look and feel that is much more visually appealing than the handcrafted one:

JOptionPane is a Façade class. A Façade class simplifies and unifies access to a larger more complex set of classes belonging to some subsystem. JOptionPane simplifies access to the Java subsystem for creating and showing dialog boxes. JOptionPane doesn’t do any of the heavy lifting of creating dialog boxes. The operations on JOptionPane simply delegate requests to existing classes.

Figure 70 JOptionPane Façade

Notice also from Figure 70 that JOptionPane doesn’t encapsulate the low-level support for creating dialog boxes. Clients are free to go around JOptionPane and create dialog boxes directly using the primitive support provided in the Java Class Library. However, it’s better to go throughJOptionPane for standard dialog boxes because it simplifies client code and makes it easier to enforce a standard look and feel.

Intent

The intent of the Façade design pattern is to “provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use” (Gamma 1995).

There are three benefits to using the Façade design pattern:

1. Unification. A Façade class defines a single point of access for clients. Clients deal with one class as opposed to a large interface or complex set of interfaces.

2. Abstraction and simplification. The interface offered by the Façade class is a level of abstraction above that of the wrapped interface. This simplifies client code.

3. Decreased coupling. A Façade class decouples clients from the details of a subsystem. Clients that access the services of a subsystem through a Façade class are buffered from changes in the subsystem.

Solution

The Façade design pattern is one of the easiest to understand and implement. It doesn’t use inheritance or polymorphism and there are no interfaces to implement. It takes only one class to implement the Façade design pattern. The class includes operations that provide a high-level interface onto an existing subsystem. The operations are implemented by delegating to lower-level components (usually classes) in the existing subsystem.

Figure 71 Façade design pattern

A Façade class doesn’t encapsulate, but rather, “wraps” an existing subsystem. “Wraps” is a better term for the relationship because clients are free to go around the Façade and access the features of the subsystem directly.

Sample Code

The sample code in this section uses the metaphor of a camera to show how much easier it is to take a picture when there is a façade class that wraps the raw camera controls. The PointAndShootFaçade, like a point and shoot camera, hides the complexity of the low-level camera interface, which includes a light meter and controls for shutter speed and aperture size. The PointAndShootFaçade doesn’t offer all the flexibility of the low-level interface but it is much easier to use when all you want to do is simply take a picture.

Figure 72 Class diagram for code sample

The low-level camera interface includes controls for shutter speed and aperture size along with a light meter service class and film component. The static method client1() takes a picture using PointAndShootFacade. The static method client2() takes a picture using the low-level camera interface. Notice how much simpler the code is for client1() compared to client2().

public class Client {

public static void main(String[] args) {

client1();

client2();

}

// client1 takes a picture using PointAndShootFacade.

public static void client1() {

PointAndShootFacade pointAndShootCamera =

new PointAndShootFacade();

pointAndShootCamera.takePicture();

}

// client2 takes a picture using the raw camera interface.

public static void client2() {

Shutter shutter = new Shutter();

Aperture aperture = new Aperture();

Film film = new Film();

// Clients that don't use the Facade interface are

// forced to understand the subtle tradeoffs

// between film speed, shutter speed and aperture size.

int filmSpeed = film.getFilmSpeed();

if (filmSpeed <= 200) {

shutter.setSpeed(1.0/30.0);

aperture.setSize(5.6);

}

else if (filmSpeed >= 400) {

shutter.setSpeed(1.0/60.0);

aperture.setSize(8);

}

else {

shutter.setSpeed(1.0/45.0);

aperture.setSize(5.6);

}

shutter.trigger();

}

}

class PointAndShootFacade {

private Shutter shutter;

private Aperture aperture;

private Film film;

public PointAndShootFacade() {

shutter = new Shutter();

aperture = new Aperture();

film = new Film();

}

public void takePicture() {

LightMeter lightMeter = new LightMeter(film.getFilmSpeed());

shutter.setSpeed(lightMeter.getRecommendedShutterSpeed());

aperture.setSize(lightMeter.getRecommendedApertureSize());

shutter.trigger(); // take the picture

}

}

class Shutter {

private double speed;

public void setSpeed(double speed) {

this.speed = speed;

}

public void trigger() {

System.out.format("Open shutter for %.3f seconds\n", speed);

}

}

class Aperture {

private double size;

public void setSize(double size) {

this.size = size;

}

}

class Film {

public int getFilmSpeed() {

return 200; // hard coded for this example

}

}

class LightMeter {

private int filmSpeed;

public LightMeter(int filmSpeed) {

this.filmSpeed = filmSpeed;

}

public double getRecommendedShutterSpeed() {

return 1.0/60.0; // hard coded for this example

}

public double getRecommendedApertureSize() {

return 5.6; // hard coded for this example

}

}

Output:

Open shutter for 0.017 seconds

Open shutter for 0.033 seconds

Discussion

The Façade design pattern is often used with the layered architecture style. One way to reduce coupling between layers is to have clients access the services of a layer through one or more Façade classes. The more components there are within a layer the greater the benefit of having a few well-defined access points.

Figure 73 Façade design pattern being used with the layered architecture style

Architectural patterns Table Data Gateway and Row Data Gateway are special cases of the Façade design pattern. They encapsulate the details of accessing the underlying database.

Related Patterns

The Façade design pattern uses object composition and delegation, as do other design patterns such as Adapter, Bridge, Proxy and Decorator. See Related Patterns under Adapter for the distinction between these structurally similar patterns.