Mediator Design Pattern in JavaScript

1. Definition

The Mediator Design Pattern defines an object that encapsulates how a set of objects interact. This pattern promotes loose coupling by ensuring that instead of objects referring to each other explicitly, their interaction is centralized through a mediator object.

2. Problem Statement

In a system, you might have multiple objects interacting with each other. Direct references between these objects might lead to dependencies that are hard to manage and may violate the Single Responsibility Principle. As the number of interactions grows, the system becomes more complex and harder to maintain.

3. Solution

Introduce a central mediator object that encapsulates the interactions between various components. Instead of the components communicating directly with each other, they communicate through the mediator. This reduces dependencies between the components.

4. Real-World Use Cases

1. Air traffic control system where the tower (mediator) ensures safe takeoffs and landings of numerous planes.

2. Chat room where multiple users send messages, but the transmission is managed by a centralized server (mediator).

3. GUI where multiple widgets interact, but their interactions are coordinated by a central controller.

5. Implementation Steps

1. Define the Mediator interface that outlines the necessary communication methods.

2. Create concrete Mediator classes implementing the Mediator interface.

3. Ensure components (Colleagues) have a reference to the mediator but not to other colleagues.

4. Colleagues communicate with the mediator when they want to send messages to other colleagues.

6. Implementation in JavaScript

// Mediator Interface
class Mediator {
    notify(sender, event) {}
}
// Concrete Mediator
class ChatRoomMediator extends Mediator {
    constructor() {
        super();
        this.participants = [];
    }
    register(participant) {
        this.participants.push(participant);
        participant.mediator = this;
    }
    notify(sender, message) {
        for (let p of this.participants) {
            if (p !== sender) {
                p.receive(message, sender);
            }
        }
    }
}
// Colleague Class
class Participant {
    constructor(name) {
        this.name = name;
        this.mediator = null;
    }
    send(message) {
        console.log(this.name + " sends: " + message);
        this.mediator.notify(this, message);
    }
    receive(message, sender) {
        console.log(this.name + " received from " + sender.name + ": " + message);
    }
}
// Client Code
const mediator = new ChatRoomMediator();
const john = new Participant("John");
const alice = new Participant("Alice");
mediator.register(john);
mediator.register(alice);
john.send("Hello, Alice!");
alice.send("Hi, John!");

Output:

John sends: Hello, Alice!
Alice received from John: Hello, Alice!
Alice sends: Hi, John!
John received from Alice: Hi, John!

Explanation:

1. We defined a Mediator interface and a concrete ChatRoomMediator which can coordinate communication between multiple Participant objects.

2. The Participant class represents individual users in the chat room. Each participant has a reference to the mediator but doesn't have direct references to other participants.

3. In the client code, we created two participants, registered them with the mediator, and simulated sending messages between them. All message passing happens through the mediator.

4. This way, if we need to change the way messaging works, we only need to adjust the mediator and not every individual participant.

7. When to use?

The Mediator Pattern is useful when:

1. A set of objects communicates in complex ways, and you want to simplify these interactions by centralizing external communications.

2. You want to reduce the dependencies between classes, ensuring that they interact only through a mediator.

3. You want to create a more decoupled system where changes in one component don't affect others.


Comments