‘Facade’ Pattern in Swift

Romain Brunie
Level Up Coding
Published in
5 min readMay 24, 2020

--

Definition

‘Facade’ pattern is a structural design pattern which improves the structure of a large system¹. Structural pattern is useful for making independent developed systems work together.

The ‘Facade’ pattern defines an object representing a subsystem. This object is an interface that will take the responsibility to interact with the subsystem.

[1] A system is composed of subsystems. A subsystem contains an interface and encapsulates classes. A class contains states and operations.

When should we use this pattern?

To provide a simple interface to a complex subsystem

This pattern should be used when it is possible to provide a simpler interface than the one provided by the existing subsystem. A complex subsystem needs to work in a specific way in order to work correctly. Moving this complexity to an interface simplifies the use of its functionalities. This interface, called a facade, hides the complexity from the client.

To decouple the subsystem from clients and other subsystems

This pattern should be used when a system has many dependent¹ subsystems. It decouples their implementation details. It reduces dependencies and makes the code easier to use, understand, maintain and test.

To define an entry point to a subsystem

This pattern should be used when subsystems are interdependent². Instead of exposing a client to a library, a framework, or a set of classes and their APIs, the facade pattern only exposes a simple unified API. Subsystems implement functionalities without knowing about the facade. They interact with each other within the system. So, the implementation details from the subsystems can change without impacting the facade API. It does not need any changes since a facade deals with interfaces and not implementations. It adds a layer of abstraction (the entry point) to improve the usability, readability and architecture of the code.

[1] Dependent subsystems mean that a system has a dependency/reference on another system.

[2] Interdependent subsystems mean that a system relies on another system in order to work.

How should we use this pattern?

A client communicates with a subsystem through a facade. It is a simple interface that delegates the work to the appropriate subsystem.

image from Head First Design Patterns by Eric Freeman, Elisabeth Freeman, Kathy Sierra and Bert Bates

If the subsystem classes can be highly-decoupled, following the Single Responsibility Principle and applying design patterns, we end up with a lot of smaller and smaller components. It makes them more reusable and easier to customize. However, depending on the number of components and the complexity of the task to achieve, it becomes harder and harder to use them.️ It might be difficult for the client to complete the task. The reason why we use the facade pattern is because it provides a simple view of the subsystem to the client.

Concrete example

Let’s say we have a home theater system that controls devices such as a DVD player, a projector… Here is the UML diagram reflecting the structure above.

We have a complex system with multiple classes interacting with each other. Before having a facade, the client had to directly interact with all of the appropriate functionalities/classes in order to start a movie for example. An analogy is to think about the facade as a remote controller.

Implementation

To implement the facade, we create a facade object with subsystem objects.

‘Facade’ Implementation

class HomeTheaterFacade {
private let amp: Amplifier
private let tuner: Tuner
private let dvd: DvdPlayer
private let cd: CdPlayer
private let projector: Projector
private let lights: TheaterLights
private let screen: Screen
private let popper: PopcornPopper
init(_ amp: Amplifier,
_ tuner: Tuner,
_ dvd: DvdPlayer,
_ cd: CdPlayer,
_ projector: Projector,
_ lights: TheaterLights,
_ screen: Screen,
_ popper: PopcornPopper) {
self.amp = amp
self.tuner = tuner
self.dvd = dvd
self.cd = cd
self.projector = projector
self.lights = lights
self.screen = screen
self.popper = popper
}
// MARK: Facade methods func watchMovie(movie: String) {
print("Get ready to watch a movie...")
popper.on()
popper.pop()
lights.dim(10)
screen.down()
projector.on()
projector.wideScreenMode()
amp.on()
amp.setDvd(dvd)
amp.setSurroundSound()
amp.setVolume(5)
dvd.on()
dvd.play(movie)
}

// other methods here
}

We compose our facade through constructor injection to give the facade all of the components it needs from the subsystem.

Client Implementation

// instantiate components herelet homeTheater = HomeTheaterFacade(amp, tuner, dvd, cd, projector, lights, screen, popper)homeTheater.watchMovie(movie: "Raiders of the Lost Ark")

Here, the client communicates with the subsystem through a simplified interface while another client can still fully use the functionality of the subsystem.

Run code in a Playground

Here is an Online Swift Playground so an Xcode Playground does not have to be created in order to test this implementation of the ‘Facade’ pattern. Then, copy the code below that corresponds with the full implementation of the ‘Facade’ pattern for our home theater system.

class Amplifier {
var tuner: Tuner
var dvdPlayer: DvdPlayer
var cdPlayer: CdPlayer
init(tuner: Tuner,
dvdPlayer: DvdPlayer,
cdPlayer: CdPlayer) {
self.tuner = tuner
self.dvdPlayer = dvdPlayer
self.cdPlayer = cdPlayer
}
func on() {
print("Amplifier on")
}
func setDvd(_ dvd: DvdPlayer) {
print("Amplifier setting DVD player")
}
func setSurroundSound() {
print("Amplifier surround sound on")
}
func setVolume(_ volume: Int) {
print("Amplifier setting volume to \(volume)")
}
// other Amplifier methods...
}
class DvdPlayer {
var movie: String = ""
func on() {
print("DVD Player on")
}
func play(_ movie: String) {
self.movie = movie
print("DVD PLayer playing \"\(movie)\"")
}
// other DvdPlayer methods...
}
class TheaterLights {
func dim(_ level: Int) {
print("Theater Ceiling Lights dimming to \(level)%")
}
func on() {
print("Theater Ceiling Lights on")
}
// other TheaterLights methods...
}
class Projector {
var dvdPlayer: DvdPlayer
init(dvdPlayer: DvdPlayer) {
self.dvdPlayer = dvdPlayer
}
func on() {
print("Projector on")
}
func wideScreenMode() {
print("Projector in widescreen mode (16x9 aspect ratio)")
}
// other Projector methods...
}
class Tuner {
// Tuner methods...
}
class Screen {
func down() {
print("Theater Screen going down")
}
// other Screen methods...
}
class PopcornPopper {
func on() {
print("Popcorn Popper on")
}
func pop() {
print("Popcorn Popper popping popcorn!")
}
// other PopcornPopper methods...
}
class CdPlayer {
// CdPlayer methods...
}
class HomeTheaterFacade {
private let amp: Amplifier
private let tuner: Tuner
private let dvd: DvdPlayer
private let cd: CdPlayer
private let projector: Projector
private let lights: TheaterLights
private let screen: Screen
private let popper: PopcornPopper
init(_ amp: Amplifier,
_ tuner: Tuner,
_ dvd: DvdPlayer,
_ cd: CdPlayer,
_ projector: Projector,
_ lights: TheaterLights,
_ screen: Screen,
_ popper: PopcornPopper) {
self.amp = amp
self.tuner = tuner
self.dvd = dvd
self.cd = cd
self.projector = projector
self.lights = lights
self.screen = screen
self.popper = popper
}
// MARK: Facade methods
func
watchMovie(movie: String) {
print("Get ready to watch a movie...")
popper.on()
popper.pop()
lights.dim(10)
screen.down()
projector.on()
projector.wideScreenMode()
amp.on()
amp.setDvd(dvd)
amp.setSurroundSound()
amp.setVolume(5)
dvd.on()
dvd.play(movie)
}
// other methods here...
}
// instantiate components here
let tuner = Tuner()
let dvd = DvdPlayer()
let cd = CdPlayer()
let amp = Amplifier(tuner: tuner, dvdPlayer: dvd, cdPlayer: cd)
let projector = Projector(dvdPlayer: dvd)
let screen = Screen()
let lights = TheaterLights()
let popper = PopcornPopper()
let homeTheater = HomeTheaterFacade(amp, tuner, dvd, cd, projector, lights, screen, popper)homeTheater.watchMovie(movie: "Raiders of the Lost Ark")

Finally, paste and run the code.

--

--