Low-level design3 min
Mediator Pattern
Type: Behavioral Pattern
Relevance: Medium. Very common in frontend development (e.g., UI components talking to a central Store/Controller) and Microservices (an API Gateway acts as a mediator).
The Mediator pattern reduces chaotic dependencies between objects. It restricts direct communications between the objects and forces them to collaborate only via a central mediator object.
Real-Life Analogy
Think of Air Traffic Control (ATC) at an airport. If an airport has 100 airplanes, you do not want Airplane A talking directly to Airplane B, C, and D to figure out who lands next. That would be chaotic. Instead, all airplanes communicate only with the ATC Tower. The ATC Tower is the Mediator. It looks at the whole picture and tells the planes what to do.
The Problem
When you build complex UIs, you might have a Button, a Checkbox, and a Text Field. If the Button needs to disable the Text Field when clicked, you end up writing textField.disable() inside the Button's code.
Soon, every UI component has direct references to every other UI component, creating a massive, tangled web of dependencies (Spaghetti Code).
How to Implement Mediator
Extract all the relationship logic into a separate Mediator class. Components (Colleagues) no longer talk to each other; they emit events to the Mediator, and the Mediator calls methods on the appropriate components.
Example in Code
// 1. The Mediator Interface
interface Mediator {
notify(sender: object, event: string): void;
}
// 2. Base Component (Holds reference to Mediator)
class BaseComponent {
protected mediator: Mediator;
constructor(mediator: Mediator = null as any) {
this.mediator = mediator;
}
public setMediator(mediator: Mediator): void {
this.mediator = mediator;
}
}
// 3. Concrete Components
class Checkbox extends BaseComponent {
public check(): void {
console.log("Checkbox was checked.");
this.mediator.notify(this, "check"); // Tells ATC!
}
}
class SubmitButton extends BaseComponent {
public enable(): void { console.log("Submit Button ENABLED."); }
public disable(): void { console.log("Submit Button DISABLED."); }
}
// 4. The Concrete Mediator (The ATC Tower)
class DialogMediator implements Mediator {
private checkbox: Checkbox;
private button: SubmitButton;
constructor(checkbox: Checkbox, button: SubmitButton) {
this.checkbox = checkbox;
this.button = button;
// Wire them up to the mediator
this.checkbox.setMediator(this);
this.button.setMediator(this);
}
// The Mediator handles the complex coordination!
public notify(sender: object, event: string): void {
if (sender === this.checkbox && event === "check") {
console.log("Mediator: Checkbox checked. Enabling Submit button.");
this.button.enable();
}
}
}
// Client Code
const checkbox = new Checkbox();
const button = new SubmitButton();
// We create the mediator and pass the components to it
const dialog = new DialogMediator(checkbox, button);
// The checkbox doesn't know the button exists.
// It just notifies the mediator!
checkbox.check();
Class Diagram
Why is it Important?
- Reduces Coupling: It replaces many-to-many relationships with one-to-many relationships, making components highly reusable.
- Centralized Logic: It puts all the complex coordination logic into one single place, making it easy to understand how the system interacts as a whole.