Skip to content

Mediator

Mediator introduces an object that encapsulates how a set of objects interact. Instead of each object holding references to all the others, they all talk to the mediator, which decides who needs to know what.

When several objects need to coordinate, the naive approach is to wire them directly to one another. In a chat room, every participant would keep a list of every other participant. In a form dialog, the submit button would reference the text field, which would reference the checkbox, which would reference the label. The number of connections grows with the square of the number of objects, and each object becomes hard to reuse because it drags its collaborators along.

Mediator collapses that web into a hub and spokes. Each colleague knows only the mediator; when something happens, it tells the mediator, and the mediator routes the consequences to whichever colleagues care. The colleagues become simpler and independently reusable, and the interaction logic lives in one place where you can read and change it. The trade is that the mediator itself can grow large if you let it absorb too much.

classDiagram
  class Mediator {
    <<interface>>
    +notify(sender, event)
  }
  class ChatRoom {
    -members: List~Colleague~
    +register(c Colleague)
    +notify(sender, event)
  }
  class Colleague {
    -mediator Mediator
    +send(msg)
    +receive(from, msg)
  }
  Mediator <|.. ChatRoom
  ChatRoom o-- Colleague : coordinates
  Colleague --> Mediator : talks to
Colleagues talk only to the mediator, which routes messages between them
  • Mediator — the interface for the hub. Colleagues call it when something happens.
  • ChatRoom — a concrete mediator. It holds the colleagues and contains the logic for routing events between them.
  • Colleague — a participant. It knows the mediator but not the other colleagues, so it stays decoupled from them.

A chat room as the mediator. Each user sends through the room, and the room broadcasts the message to every other user — no user ever references another user directly.

interface ChatMediator {
register(user: User): void;
broadcast(from: User, message: string): void;
}
class ChatRoom implements ChatMediator {
private users: User[] = [];
register(user: User): void {
this.users.push(user);
}
broadcast(from: User, message: string): void {
for (const user of this.users) {
if (user !== from) user.receive(from.name, message);
}
}
}
class User {
constructor(readonly name: string, private room: ChatMediator) {
room.register(this);
}
send(message: string): void {
this.room.broadcast(this, message);
}
receive(from: string, message: string): void {
console.log(`[${this.name}] ${from}: ${message}`);
}
}
const room = new ChatRoom();
const alice = new User('Alice', room);
const bob = new User('Bob', room);
alice.send('hi'); // [Bob] Alice: hi
bob.send('hello'); // [Alice] Bob: hello
  • Pro: turns a tangled many-to-many web of references into simple hub-and-spoke connections.
  • Pro: colleagues become reusable because they depend only on the mediator interface, not on each other.
  • Pro: the interaction logic lives in one readable place rather than scattered across colleagues.
  • Con: the mediator can swell into a god object that knows everything, becoming the very complexity you tried to remove.
  • Con: it adds a layer of indirection that can obscure who ultimately reacts to an event.
  • Observer also decouples senders from receivers, but it broadcasts to subscribers rather than centralizing arbitrary routing logic.
  • Facade likewise offers one point of contact, but a facade only simplifies one-way calls into a subsystem, whereas a mediator coordinates two-way traffic between peers.
What does a Mediator centralize?
How do colleagues refer to one another under the Mediator pattern?
What is the main risk of the Mediator pattern?