Mediator Design Pattern in Swift

1. Definition

The Mediator pattern provides a unified interface to a set of interfaces in a subsystem, thereby promoting loose coupling by keeping objects from referring to each other explicitly. Instead of objects communicating directly with each other, they communicate through the mediator.

2. Problem Statement

Consider a complex system with many interacting components. Direct communication between components leads to tight coupling, making the system hard to maintain, understand, and extend.

3. Solution

Introduce a mediator object that centralizes external communications. Instead of components communicating directly, they send and receive messages through the mediator.

4. Real-World Use Cases

1. Air traffic control centers mediating communication between planes.

2. Chat rooms where users send messages to the room, and the room broadcasts to all participants.

3. GUI where buttons and input fields communicate via a form object.

5. Implementation Steps

1. Define a Mediator protocol which will declare methods for communicating with colleagues.

2. Create concrete Mediator classes.

3. Define Colleague classes which will communicate through the mediator.

6. Implementation in Swift Programming

// 1. Mediator Protocol
protocol Mediator {
    func send(message: String, colleague: Colleague)
}
// 2. Concrete Mediator
class ConcreteMediator: Mediator {
    private var colleagues: [Colleague] = []
    func register(colleague: Colleague) {
        colleagues.append(colleague)
    }
    func send(message: String, colleague: Colleague) {
        for c in colleagues where c !== colleague {
            c.receive(message: message)
        }
    }
}
// 3. Colleague Protocol & Concrete Colleague
protocol Colleague: AnyObject {
    var mediator: Mediator { get }
    func receive(message: String)
}
class ConcreteColleague: Colleague {
    let mediator: Mediator
    init(mediator: Mediator) {
        self.mediator = mediator
    }
    func send(message: String) {
        mediator.send(message: message, colleague: self)
    }
    func receive(message: String) {
        print("Received: \(message)")
    }
}
// 4. Using the Mediator
let mediator = ConcreteMediator()
let colleague1 = ConcreteColleague(mediator: mediator)
let colleague2 = ConcreteColleague(mediator: mediator)
mediator.register(colleague: colleague1)
mediator.register(colleague: colleague2)
colleague1.send(message: "Hello from Colleague 1!")

Output:

Received: Hello from Colleague 1!

Explanation:

1. We defined a Mediator protocol and a concrete implementation, ConcreteMediator.

2. ConcreteMediator keeps track of colleagues and broadcasts messages to all of them except the sender.

3. Colleague classes send and receive messages but do so through the mediator rather than directly with each other.

7. When to use?

Use the Mediator pattern when:

1. A set of objects communicate in well-defined but complex ways, and you want to avoid tight coupling.

2. Reusing an object is difficult because it communicates with many other objects.

3. Communication logic is distributed among several objects and should be centralized.


Comments