070
creational structural

Facade

Reference Wikipedia ↗
Facade — class diagram
Plate 070 class diagram

The Facade pattern provides a simplified interface to a complex system of classes, objects, and subsystems. It abstracts away the intricacies of the underlying components, offering clients a higher-level, easier-to-use entry point. Essentially, it’s an “entry point” object that encapsulates the interaction with multiple system parts.

This pattern is useful when you want to reduce complexity for clients, promote loose coupling, and offer a more intuitive way to interact with a system. It’s commonly used in scenarios where a system has multiple dependencies or where the client only needs a limited set of features from a larger system. It’s also valuable when migrating to a new subsystem, as the facade can provide compatibility with legacy code while the internal workings change.

Usage

The Facade pattern is commonly found in:

  • Complex Libraries/Frameworks: Providing a simple API to interact with a large and intricate codebase.
  • System Integration: Abstracting the interaction with different, potentially incompatible systems.
  • Layered Architectures: Acting as a gateway to a lower layer from a higher layer, shielding the higher layer from implementation details.
  • Build Systems: Managing complex compilation and linking processes with a simplified command.

Examples

  1. Apache Camel: Camel uses facades extensively to provide simplified integration patterns for connecting different systems (e.g., databases, message queues, web services). Instead of directly interacting with the underlying transport mechanisms, you define routes using Camel’s DSL, and the facade handles the complexities of the underlying integrations. The ProducerTemplate and ConsumerTemplate classes provide facades for sending and receiving messages.

  2. Java Database Connectivity (JDBC): JDBC provides a facade over various database APIs. Developers interact with the database through the Connection, Statement, and ResultSet interfaces, without needing to know the specific details of how each database vendor implements these functionalities internally. The DriverManager class serves as a facade, simplifying the process of obtaining a database connection.

  3. Docker SDKs: Docker SDKs for languages like Python and Java offer a facade over the Docker Engine API. You don’t need to learn the intricacies of the Docker Engine’s CLI or REST API; instead, you use high-level functions provided by the SDK to manage containers, images, and networks.

Specimens

15 implementations
Specimen 070.01 Dart View specimen ↗

The Facade pattern provides a simplified interface to a complex subsystem. It defines a high-level interface that hides the complexities of the lower-level system. This makes the subsystem easier to use and understand for clients.

The Dart code demonstrates a media player facade, simplifying interactions with components like file system, audio decoder, and video renderer. The MediaPlayer class offers methods like play() and stop() which internally orchestrate the complex operations of these subsystems. This approach encapsulates the complexity and presents a clean, easy-to-use API. Dart’s class-based structure naturally lends itself to the Facade pattern, allowing for clear encapsulation and a well-defined interface.

// Subsystem 1: File System
class FileSystem {
  String readFile(String filePath) {
    print('Reading file from: $filePath');
    return 'File content from $filePath';
  }
}

// Subsystem 2: Audio Decoder
class AudioDecoder {
  String decode(String data) {
    print('Decoding audio data');
    return 'Decoded audio: $data';
  }
}

// Subsystem 3: Video Renderer
class VideoRenderer {
  void renderVideo(String data) {
    print('Rendering video: $data');
  }
}

// Facade: MediaPlayer
class MediaPlayer {
  final FileSystem _fileSystem = FileSystem();
  final AudioDecoder _audioDecoder = AudioDecoder();
  final VideoRenderer _videoRenderer = VideoRenderer();

  void play(String filePath) {
    print('Playing media from: $filePath');
    String fileContent = _fileSystem.readFile(filePath);
    String decodedAudio = _audioDecoder.decode(fileContent);
    _videoRenderer.renderVideo(decodedAudio);
    print('Media playing...');
  }

  void stop() {
    print('Stopping media...');
    // Add stop logic for subsystems if needed
  }
}

// Client code
void main() {
  final mediaPlayer = MediaPlayer();
  mediaPlayer.play('path/to/media.file');
  mediaPlayer.stop();
}