Reduce chaotic dependencies between objects
Swift — Problems Catalogue #18
Problem Definition:
Consider the following scenario. You have to implement an air traffic control simulation app where users can control different aircraft and communicate with other aircraft.
The app would have a main interface where users can select an aircraft and send messages to other aircraft, such as requesting permission to take off or land. Due to the fact that there is a lot of aircraft that need to talk with one another, we can quickly slip into references hell and resulting in a highly tightly coupled system.
We need a way to allow loose coupling between all aircraft in order to make it easy to extend or modify.
Problem Solution:
Solution —Mediator it’s a design pattern that is used to manage communication between objects and promote loose coupling between them.
Real-World Usage:
First, we need to define aMediator
protocol with two methods:registerAircraft(aircraft: Aircraft)
and send(message: String, aircraft: Aircraft)
that will be implemented by concrete mediator classes.
Next, we need to define anAircraft
protocol that has two methods: send(message: String)
and receive(message: String)
that will be implemented by concrete aircraft classes.
Now, we need to implement theAirTrafficControlMediator
class, which is the concrete implementation of the Mediator
protocol and holds references to Aircraft
objects and it forwards messages between them.
When an aircraft wants to send a message, it calls the send(message: String)
method on itself, which in turn calls the send(message: String, aircraft: Aircraft)
method on the AirTrafficControlMediator
object. The AirTrafficControlMediator
then forwards the message to all other aircraft, except the one that sent the message.
The AirTrafficControlMediator
also keeps track of all the aircraft that have been registered with it, so it knows where to forward the messages.
The Boeing747
and AirbusA320
classes implement the Aircraft
protocol and they hold a reference to an AirTrafficControlMediator
object.
Finally, let’s bring everything together, let’s create an instance of the AirTrafficControlMediator()
and pass it to the instances of the Boeing747
& AirbusA320
.
When theboeing.send(message: "Requesting permission to land.")
method is called, which in turn calls the send(message: String)
method on the boeing
object. The boeing
object then forwards the message to the AirTrafficControlMediator
object by calling the send(message: String, aircraft: Aircraft)
method. The AirTrafficControlMediator
forwards the message to all other aircrafts by calling the receive(message: String)
method on them.
By using a mediator, the aircraft objects (ex: boeing, airbus etc) don’t need to know about each other in order to communicate and they don’t have direct reference to one another, all communication goes through the mediator.
From this point on, the sky is the limit 🚀 well…almost.
Of course, this design pattern has its limitations but used in moderation, it’s a great tool in our development toolbox.
This is the seventh article in the Swift Problems Catalogue series in which I’ll tackle general software development problems. The aim is to have a quick reference guide that can be easily accessed when having a design/algorithm dillemma.
Let me know what you think and don’t be shy to share where and when this pattern simplified your coding experience 🎶