Facade Design Pattern (Structural)
Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes.
Problem
Section titled βProblemβImagine that you must make your code work with a broad set of objects that belong to a sophisticated library or framework. Ordinarily, youβd need to initialize all of those objects, keep track of dependencies, execute methods in the correct order, and so on.
As a result, the business logic of your classes would become tightly coupled to the implementation details of 3rd-party classes, making it hard to comprehend and maintain.
Solution
Section titled βSolutionβA facade is a class that provides a simple interface to a complex subsystem which contains lots of moving parts. A facade might provide limited functionality in comparison to working with the subsystem directly. However, it includes only those features that clients really care about.
Having a facade is handy when you need to integrate your app with a sophisticated library that has dozens of features, but you need only a tiny fraction of its functionality.
For instance, an app that uploads short funny videos with cats to social media could potentially use a professional video conversion library. However, all that it really needs is a class with the single method encode(filename, format). After creating such a class and connecting it with the video conversion library, youβll have your first facade.
Real-World Analogy
Section titled βReal-World AnalogyβWhen you call a shop to place a phone order, an operator is your facade to all services and departments of the shop. The operator provides you with a simple voice interface to the ordering system, payment gateways, and various delivery services.
Structure
Section titled βStructureβ- Facade provides convenient access to a particular part of the subsystemβs functionality. It knows where to direct the clientβs request and how to operate all the moving parts.
- Additional Facade classes can be created to prevent polluting a single facade with unrelated features that might make it yet another complex structure. Additional facades can be used by both clients and other facades.
- Complex Subsystem consists of dozens of various objects. To make them all do something meaningful, you have to dive deep into the subsystemβs implementation details, such as initializing objects in the correct order and supplying them with data in the proper format. Subsystem classes arenβt aware of the facadeβs existence. They operate within the system and work with each other directly.
- Client uses the facade instead of calling the subsystem objects directly.
Structure (Class Diagram)
Section titled βStructure (Class Diagram)βclassDiagram
class Client {
}
class Facade {
-subsystemA: SubsystemA
-subsystemB: SubsystemB
+operation()
}
class SubsystemA {
+operationA()
}
class SubsystemB {
+operationB()
}
Client --> Facade : calls
Facade --> SubsystemA : coordinates
Facade --> SubsystemB : coordinates
Pseudocode
Section titled βPseudocodeβ# These are some of the classes of a complex 3rd-party video# conversion framework. We don't control that code, so we# can't simplify it.
class VideoFileclass OggCompressionCodecclass MPEG4CompressionCodecclass CodecFactoryclass BitrateReaderclass AudioMixer
# We create a Facade class to hide the framework's complexity# behind a simple interface. It's a trade-off between# functionality and simplicity.class VideoConverter: method convert(filename, format): file = new VideoFile(filename) sourceCodec = (new CodecFactory()).extract(file) if (format == "mp4"): destinationCodec = new MPEG4CompressionCodec() else: destinationCodec = new OggCompressionCodec() buffer = BitrateReader.read(filename, sourceCodec) result = BitrateReader.convert(buffer, destinationCodec) result = (new AudioMixer()).fix(result) return new File(result)
# Application classes don't depend on a billion classes# provided by the complex framework. Also, if you decide to# switch frameworks, you only need to rewrite the facade class.class Application: method main(): convertor = new VideoConverter() mp4 = convertor.convert("funny-cats-video.ogg", "mp4") mp4.save()Applicability
Section titled βApplicabilityβ- Use the Facade pattern when you need to have a limited but straightforward interface to a complex subsystem.
- Use the Facade when you want to structure a subsystem into layers.
How to Implement
Section titled βHow to Implementβ- Check whether itβs possible to provide a simpler interface than what an existing subsystem already provides. Youβre on the right track if this interface makes the client code independent from many of the subsystemβs classes.
- Declare and implement this interface in a new facade class. The facade should redirect the calls from the client code to the appropriate objects of the subsystem. The facade should be responsible for initializing the subsystem and managing its further life cycle unless the client code already does this.
- To get the full benefit from the pattern, make all the client code communicate with the subsystem only via the facade. Now the client code is protected from any changes in the subsystem code.
- If the facade becomes too big, consider extracting part of its behavior into a new, refined facade class.
Pros and Cons
Section titled βPros and ConsβPros:
- You can isolate your code from the complexity of a subsystem.
Cons:
- A facade can become a god object coupled to all classes of an app.
Relations with other Design Patterns
Section titled βRelations with other Design Patternsβ- Facade defines a new interface for existing objects, whereas Adapter tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects.
- Abstract Factory can serve as an alternative to Facade when you only want to hide the way the subsystem objects are created from the client code.
- Facade is often a Singleton since a single facade object is sufficient in most cases.
Code Examples
Section titled βCode ExamplesβCode Examples (C#)
Section titled βCode Examples (C#)β// Subsystem 1public class AudioSystem{ public void TurnOn() => Console.WriteLine("Audio enabled"); public void SetVolume(int level) => Console.WriteLine($"Volume: {level}");}
// Subsystem 2public class VideoSystem{ public void TurnOn() => Console.WriteLine("Video linked"); public void SetResolution(string res) => Console.WriteLine($"Resolution: {res}");}
// Subsystem 3public class Lights{ public void Dim(int level) => Console.WriteLine($"Lights reduced to {level}%");}
// Facadepublic class HomeTheaterFacade{ private readonly AudioSystem _audio; private readonly VideoSystem _video; private readonly Lights _lights;
public HomeTheaterFacade(AudioSystem audio, VideoSystem video, Lights lights) { _audio = audio; _video = video; _lights = lights; }
public void WatchMovie() { Console.WriteLine("Getting ready to watch a movie..."); _lights.Dim(20); _audio.TurnOn(); _audio.SetVolume(15); _video.TurnOn(); _video.SetResolution("1080p"); }}
// Usageclass Program{ static void Main() { var facade = new HomeTheaterFacade( new AudioSystem(), new VideoSystem(), new Lights() );
facade.WatchMovie(); }}Code Examples (Python)
Section titled βCode Examples (Python)βfrom __future__ import annotations
class Facade: """ The Facade class provides a simple interface to the complex logic of one or several subsystems. The Facade delegates the client requests to the appropriate objects within the subsystem. The Facade is also responsible for managing their lifecycle. All of this shields the client from the undesired complexity of the subsystem. """
def __init__(self, subsystem1: Subsystem1 = None, subsystem2: Subsystem2 = None) -> None: """ Depending on your application's needs, you can provide the Facade with existing subsystem objects or force the Facade to create them on its own. """ self._subsystem1 = subsystem1 or Subsystem1() self._subsystem2 = subsystem2 or Subsystem2()
def operation(self) -> str: """ The Facade's methods are convenient shortcuts to the sophisticated functionality of the subsystems. However, clients get only to a fraction of a subsystem's capabilities. """ results = [] results.append("Facade initializes subsystems:") results.append(self._subsystem1.operation1()) results.append(self._subsystem2.operation1()) results.append("Facade orders subsystems to perform the action:") results.append(self._subsystem1.operation_n()) results.append(self._subsystem2.operation_z()) return "\\n".join(results)
class Subsystem1: def operation1(self) -> str: return "Subsystem1: Ready!"
def operation_n(self) -> str: return "Subsystem1: Go!"
class Subsystem2: def operation1(self) -> str: return "Subsystem2: Get ready!"
def operation_z(self) -> str: return "Subsystem2: Fire!"
def client_code(facade: Facade) -> None: """ The client code works with complex subsystems through a simple interface provided by the Facade. When a facade manages the lifecycle of the subsystem, the client might not even know about the existence of the subsystem. This approach lets you keep the complexity under control. """ print(facade.operation(), end="")
if __name__ == "__main__": subsystem1 = Subsystem1() subsystem2 = Subsystem2() facade = Facade(subsystem1, subsystem2) client_code(facade)Code Examples (C++)
Section titled βCode Examples (C++)β#include <iostream>#include <string>
// ============================// Subsystem 1// ============================class AudioSystem {public: void TurnOn() { std::cout << "Audio enabled" << std::endl; }
void SetVolume(int level) { std::cout << "Volume: " << level << std::endl; }};
// ============================// Subsystem 2// ============================class VideoSystem {public: void TurnOn() { std::cout << "Video linked" << std::endl; }
void SetResolution(const std::string& res) { std::cout << "Resolution: " << res << std::endl; }};
// ============================// Subsystem 3// ============================class Lights {public: void Dim(int level) { std::cout << "Lights reduced to " << level << "%" << std::endl; }};
// ============================// Facade// ============================class HomeTheaterFacade {private: AudioSystem& audio; VideoSystem& video; Lights& lights;
public: HomeTheaterFacade(AudioSystem& a, VideoSystem& v, Lights& l) : audio(a), video(v), lights(l) {}
void WatchMovie() { std::cout << "Getting ready to watch a movie..." << std::endl; lights.Dim(20); audio.TurnOn(); audio.SetVolume(15); video.TurnOn(); video.SetResolution("1080p"); }};
// ============================// Usage// ============================int main() { AudioSystem audio; VideoSystem video; Lights lights;
HomeTheaterFacade facade(audio, video, lights); facade.WatchMovie();
return 0;}Code Examples (Java)
Section titled βCode Examples (Java)β// Subsystem 1 β AudioSystempublic class AudioSystem {
public void turnOn() { System.out.println("Audio enabled"); }
public void setVolume(int level) { System.out.println("Volume: " + level); }}
// Subsystem 2 β VideoSystempublic class VideoSystem {
public void turnOn() { System.out.println("Video linked"); }
public void setResolution(String res) { System.out.println("Resolution: " + res); }}
// Subsystem 3 β Lightspublic class Lights {
public void dim(int level) { System.out.println("Lights reduced to " + level + "%"); }}
// Facade β HomeTheaterFacadepublic class HomeTheaterFacade {
private final AudioSystem audio; private final VideoSystem video; private final Lights lights;
public HomeTheaterFacade(AudioSystem audio, VideoSystem video, Lights lights) { this.audio = audio; this.video = video; this.lights = lights; }
public void watchMovie() { System.out.println("Getting ready to watch a movie..."); lights.dim(20); audio.turnOn(); audio.setVolume(15); video.turnOn(); video.setResolution("1080p"); }}
// Usagepublic class Program { public static void main(String[] args) { HomeTheaterFacade facade = new HomeTheaterFacade( new AudioSystem(), new VideoSystem(), new Lights() );
facade.watchMovie(); }}Code Examples (JavaScript)
Section titled βCode Examples (JavaScript)β// Subsystem 1 β AudioSystemclass AudioSystem { turnOn() { console.log("Audio enabled"); }
setVolume(level) { console.log(`Volume: ${level}`); }}
// Subsystem 2 β VideoSystemclass VideoSystem { turnOn() { console.log("Video linked"); }
setResolution(res) { console.log(`Resolution: ${res}`); }}
// Subsystem 3 β Lightsclass Lights { dim(level) { console.log(`Lights reduced to ${level}%`); }}
// Facade β HomeTheaterFacadeclass HomeTheaterFacade { constructor(audio, video, lights) { this.audio = audio; this.video = video; this.lights = lights; }
watchMovie() { console.log("Getting ready to watch a movie..."); this.lights.dim(20); this.audio.turnOn(); this.audio.setVolume(15); this.video.turnOn(); this.video.setResolution("1080p"); }}
// Usageconst facade = new HomeTheaterFacade( new AudioSystem(), new VideoSystem(), new Lights());
facade.watchMovie();Code Examples (Visual Basic)
Section titled βCode Examples (Visual Basic)β' ============================' Subsystem 1' ============================Public Class AudioSystem Public Sub TurnOn() Console.WriteLine("Audio enabled") End Sub
Public Sub SetVolume(level As Integer) Console.WriteLine($"Volume: {level}") End SubEnd Class
' ============================' Subsystem 2' ============================Public Class VideoSystem Public Sub TurnOn() Console.WriteLine("Video linked") End Sub
Public Sub SetResolution(res As String) Console.WriteLine($"Resolution: {res}") End SubEnd Class
' ============================' Subsystem 3' ============================Public Class Lights Public Sub Dim(level As Integer) Console.WriteLine($"Lights reduced to {level}%") End SubEnd Class
' ============================' Facade' ============================Public Class HomeTheaterFacade Private ReadOnly _audio As AudioSystem Private ReadOnly _video As VideoSystem Private ReadOnly _lights As Lights
Public Sub New(audio As AudioSystem, video As VideoSystem, lights As Lights) _audio = audio _video = video _lights = lights End Sub
Public Sub WatchMovie() Console.WriteLine("Getting ready to watch a movie...") _lights.Dim(20) _audio.TurnOn() _audio.SetVolume(15) _video.TurnOn() _video.SetResolution("1080p") End SubEnd Class
' ============================' Usage' ============================Module Program Sub Main() Dim facade As New HomeTheaterFacade( New AudioSystem(), New VideoSystem(), New Lights() )
facade.WatchMovie() End SubEnd ModuleCode Examples (Rust)
Section titled βCode Examples (Rust)β// Subsystem 1struct AudioSystem;
impl AudioSystem { fn turn_on(&self) { println!("Audio enabled"); }
fn set_volume(&self, level: i32) { println!("Volume: {}", level); }}
// Subsystem 2struct VideoSystem;
impl VideoSystem { fn turn_on(&self) { println!("Video linked"); }
fn set_resolution(&self, res: &str) { println!("Resolution: {}", res); }}
// Subsystem 3struct Lights;
impl Lights { fn dim(&self, level: i32) { println!("Lights reduced to {}%", level); }}
// Facadestruct HomeTheaterFacade { audio: AudioSystem, video: VideoSystem, lights: Lights,}
impl HomeTheaterFacade { fn new(audio: AudioSystem, video: VideoSystem, lights: Lights) -> Self { Self { audio, video, lights } }
fn watch_movie(&self) { println!("Getting ready to watch a movie..."); self.lights.dim(20); self.audio.turn_on(); self.audio.set_volume(15); self.video.turn_on(); self.video.set_resolution("1080p"); }}
// Usagefn main() { let facade = HomeTheaterFacade::new( AudioSystem, VideoSystem, Lights, );
facade.watch_movie();}Code Examples (PHP)
Section titled βCode Examples (PHP)β<?php
// Subsystem 1class AudioSystem{ public function turnOn(): void { echo "Audio enabled\n"; }
public function setVolume(int $level): void { echo "Volume: {$level}\n"; }}
// Subsystem 2class VideoSystem{ public function turnOn(): void { echo "Video linked\n"; }
public function setResolution(string $res): void { echo "Resolution: {$res}\n"; }}
// Subsystem 3class Lights{ public function dim(int $level): void { echo "Lights reduced to {$level}%\n"; }}
// Facadeclass HomeTheaterFacade{ private AudioSystem $audio; private VideoSystem $video; private Lights $lights;
public function __construct(AudioSystem $audio, VideoSystem $video, Lights $lights) { $this->audio = $audio; $this->video = $video; $this->lights = $lights; }
public function watchMovie(): void { echo "Getting ready to watch a movie...\n"; $this->lights->dim(20); $this->audio->turnOn(); $this->audio->setVolume(15); $this->video->turnOn(); $this->video->setResolution("1080p"); }}
// Usage$facade = new HomeTheaterFacade( new AudioSystem(), new VideoSystem(), new Lights());
$facade->watchMovie();Code Examples (Go)
Section titled βCode Examples (Go)βpackage main
import ( "fmt")
// Subsystem 1type AudioSystem struct{}
func (AudioSystem) TurnOn() { fmt.Println("Audio enabled")}
func (AudioSystem) SetVolume(level int) { fmt.Printf("Volume: %d\n", level)}
// Subsystem 2type VideoSystem struct{}
func (VideoSystem) TurnOn() { fmt.Println("Video linked")}
func (VideoSystem) SetResolution(res string) { fmt.Printf("Resolution: %s\n", res)}
// Subsystem 3type Lights struct{}
func (Lights) Dim(level int) { fmt.Printf("Lights reduced to %d%%\n", level)}
// Facadetype HomeTheaterFacade struct { audio AudioSystem video VideoSystem lights Lights}
func NewHomeTheaterFacade(audio AudioSystem, video VideoSystem, lights Lights) *HomeTheaterFacade { return &HomeTheaterFacade{ audio: audio, video: video, lights: lights, }}
func (h *HomeTheaterFacade) WatchMovie() { fmt.Println("Getting ready to watch a movie...") h.lights.Dim(20) h.audio.TurnOn() h.audio.SetVolume(15) h.video.TurnOn() h.video.SetResolution("1080p")}
// Usagefunc main() { facade := NewHomeTheaterFacade( AudioSystem{}, VideoSystem{}, Lights{}, )
facade.WatchMovie()}Code Examples (Ruby)
Section titled βCode Examples (Ruby)β# Subsystem 1class AudioSystemdef turn_on puts "Audio enabled"end
def set_volume(level) puts "Volume: #{level}"endend
# Subsystem 2class VideoSystemdef turn_on puts "Video linked"end
def set_resolution(res) puts "Resolution: #{res}"endend
# Subsystem 3class Lightsdef dim(level) puts "Lights reduced to #{level}%"endend
# Facadeclass HomeTheaterFacadedef initialize(audio, video, lights) @audio = audio @video = video @lights = lightsend
def watch_movie puts "Getting ready to watch a movie..." @lights.dim(20) @audio.turn_on @audio.set_volume(15) @video.turn_on @video.set_resolution("1080p")endend
# Usagefacade = HomeTheaterFacade.new(AudioSystem.new,VideoSystem.new,Lights.new)
facade.watch_movieCode Examples (Perl)
Section titled βCode Examples (Perl)β#!/usr/bin/perluse strict;use warnings;
############################# Subsystem 1############################package AudioSystem;
sub new { bless {}, shift }
sub turn_on { print "Audio enabled\n";}
sub set_volume { my ($self, $level) = @_; print "Volume: $level\n";}
############################# Subsystem 2############################package VideoSystem;
sub new { bless {}, shift }
sub turn_on { print "Video linked\n";}
sub set_resolution { my ($self, $res) = @_; print "Resolution: $res\n";}
############################# Subsystem 3############################package Lights;
sub new { bless {}, shift }
sub dim { my ($self, $level) = @_; print "Lights reduced to $level%\n";}
############################# Facade############################package HomeTheaterFacade;
sub new { my ($class, $audio, $video, $lights) = @_; return bless { audio => $audio, video => $video, lights => $lights, }, $class;}
sub watch_movie { my ($self) = @_; print "Getting ready to watch a movie...\n"; $self->{lights}->dim(20); $self->{audio}->turn_on(); $self->{audio}->set_volume(15); $self->{video}->turn_on(); $self->{video}->set_resolution("1080p");}
############################# Usage############################package main;
my $facade = HomeTheaterFacade->new( AudioSystem->new(), VideoSystem->new(), Lights->new());
$facade->watch_movie();Code Examples (TypeScript)
Section titled βCode Examples (TypeScript)β// Subsystem 1 β AudioSystemclass AudioSystem { turnOn(): void { console.log("Audio enabled"); }
setVolume(level: number): void { console.log(`Volume: ${level}`); }}
// Subsystem 2 β VideoSystemclass VideoSystem { turnOn(): void { console.log("Video linked"); }
setResolution(resolution: string): void { console.log(`Resolution: ${resolution}`); }}
// Subsystem 3 β Lightsclass Lights { dim(level: number): void { console.log(`Lights reduced to ${level}%`); }}
// Facade β HomeTheaterFacadeclass HomeTheaterFacade { private audio: AudioSystem; private video: VideoSystem; private lights: Lights;
constructor(audio: AudioSystem, video: VideoSystem, lights: Lights) { this.audio = audio; this.video = video; this.lights = lights; }
watchMovie(): void { console.log("Getting ready to watch a movie..."); this.lights.dim(20); this.audio.turnOn(); this.audio.setVolume(15); this.video.turnOn(); this.video.setResolution("1080p"); }}
// Usageconst facade = new HomeTheaterFacade( new AudioSystem(), new VideoSystem(), new Lights());
facade.watchMovie();