Structural Design Patterns for Dart and Flutter: Façade

In cases where you have a large number of subsystems, each with its own simple or complicated interface, it can sometimes be to your advantage to abstract away those interfaces by unifying them under one common, usually simpler interface. This is the essence of the Façade pattern. Through the unified interface, client code can interact more easily with the subsystems without reducing the options provided. The pattern also serves to shield client code from subsystem details, promoting loose coupling. Additionally, the Façade pattern is sometimes used to wrap a complex API with a simpler version, exposing only the required functionality or combining several operations into one.

About structural design patterns: Structural patterns help us shape the relationships between the objects and classes we create. These patterns are focused on how classes inherit from each other, how objects can be composed of other objects, and how objects and classes interrelate. In this series of articles, you'll learn how to build large, comprehensive systems from simpler, individual modules and components. The patterns assist us in creating flexible, loosely coupled, interconnecting code modules to complete complex tasks in a manageable way.
The code for this article was tested with Dart 2.8.4 and Flutter 1.17.5.

Let's explore the Façade pattern with a code structure you might see in a Dart or Flutter app built to control a smart home.

A Façade example

A typical smart home has a number of complex systems that need to be managed, including lights, security, window shades, maybe an intercom system, and possibly more. Each of these aspects of the home would likely have its own app screen or screens to manage every detail, such as whether each is on or off, open or closed, and each may support more advanced features like timers or alarms. You might use the Façade pattern to create a simplified interface that can manage all the systems at a high level from a single screen, as shown in the following diagram:

Each subsystem class in this example has just a few methods to keep things brief, and there could potentially be many more subsystems, but even this abbreviated scenario demonstrates the value of a good façade. The SmartHome class acts as a façade that greatly simplifies the management of its multiple subsystems.

Here are the skeletal versions of these subsystem classes as Dart code:

class SecuritySystem {
  void enable() => print("SecuritySystem enabled");
  void disable() => print("SecuritySystem disabled");
}

class Intercom {
  void on()  => print("Intercom ready");
  void off() => print("Intercom standing by");
}

class WindowShades {
  void open() => print("WindowShades open");
  void close() => print("WindowShades closed");
}

class Lights {
  void on() => print("Lights on");
  void off() => print("Lights off");
}

Each class has only two methods, and for illustration purposes, each does nothing more than output some text to the debug console. The real star of the example is the SmartHome class, which serves as a façade that facilitates access to the subsystem features:

class SmartHome {
  SecuritySystem _securitySystem = SecuritySystem();
  Intercom _intercom = Intercom();
  WindowShades _windowShades = WindowShades();
  Lights _lights = Lights();

  void home() {
    _securitySystem.disable();
    _intercom.on();
    _windowShades.open();
    _lights.on();
  }

  void out() {
    _securitySystem.enable();
    _intercom.off();
    _windowShades.close();
    _lights.off();
  }
}

SmartHome keeps private references to each of the home's subsystems, then provides shortcut methods to be used when the homeowner arrives home or goes out. A user interface might allow a user to inform the app of their arrivals and departures, calling home() and out(). These methods handle the gritty details of all the individual systems, and as a bonus, the implementations of these subsystems are encapsulated, which allows those details to be updated without breaking the client code that uses SmartHome.

It's not difficult to imagine other high-level methods you could add to this façade. It might support more scenarios, such as when the app's user is having a party, quietly studying, or having a romantic evening. The Lights subsystem may have its own specific façade class for controlling all the lights on a certain floor, or just the children's rooms, or all outdoor lighting, etc. Quite possibly, the SmartHome façade would keep a reference to a Lights façade in addition to, or instead of, the Lights subsystem class.

Conclusion

The Façade pattern presents a simplified interface to a more complex subsystem or subsystems. To read more about structural design patterns in Dart, check out these related articles: