The idea
What it is
Mediator answers one question: what do you do when a group of objects all need to talk to each other? The naive answer — give every object a reference to every other object — works for two or three, then rots. Each object must know all its peers, message them, and react to them; change one and you touch them all. The intent of Mediator, in one sentence: centralize that tangled object-to-object communication in a single mediator object, so each colleague only knows the hub — never each other.
The classic picture is air traffic control. Dozens of planes approach one airport, and they absolutely must coordinate — but they never negotiate with each other pairwise ('you land first, I'll circle'). Every plane talks only to the tower, and the tower coordinates all of them. In code, the same shape appears in a chat room: users don't hold references to every other user; each user just calls send() on the ChatRoom, and the room decides who receives the message. The planes and the users are the colleagues; the tower and the room are the mediator.
The one sentence to remember
Mediator replaces many-to-many links between objects with many-to-one links to a hub. Colleagues stop saying 'hey Ben, hey Chloe, hey Dan…' and start saying 'hey room, deliver this' — the room knows who's there and routes it.
Why the difference explodes with size
With direct links, n objects need up to n(n−1)/2 connections — 4 objects need 6, 10 objects need 45, 50 objects need 1,225. With a mediator, n objects need exactly n connections. Adding one more colleague costs one new link instead of n new ones. The prototype's connections counter makes this painfully visible.
Mechanics
How it works
The three participants
A textbook Mediator has three roles, and the discipline that makes it work is brutally simple: colleagues hold a reference to the mediator and nothing else.
- Mediator interface — declares how colleagues talk to the hub, typically one method like
send(message, from). Colleagues depend only on this interface, so you can swap in a different concrete mediator (a logging room, a moderated room) without touching them. - Concrete Mediator (
ChatRoom, the control tower) — keeps the list of colleagues and owns all the coordination logic: who receives what, in what order, under what rules. This is the one place where 'who talks to whom' lives. - Colleagues (
User, a plane) — each holds a single reference to the mediator. To communicate, a colleague calls the mediator; to be reached, it exposes a method the mediator calls back (likereceive()). A colleague never names another colleague.
The connection math
This is the heart of the pattern. Fully meshed, n colleagues need n(n−1)/2 links, because every pair gets its own connection — and every existing object changes when a new one joins, since each must learn about the newcomer. With a hub, n colleagues need exactly n links, and a newcomer costs exactly one new link — registration with the mediator. Nobody else is edited, recompiled, or even aware.
There's a second, quieter win: colleagues become reusable. A User class that hard-codes ben.receive(...) and chloe.receive(...) can only live in this exact chat. A User that only knows 'some mediator' can be dropped into any room, any test harness, any future feature — because it never names its peers. Low coupling between colleagues is bought by pointing all the coupling at one hub (see [[coupling-and-cohesion]]).
interface ChatMediator { // the Mediator interface
send(message: string, from: User): void;
}
class ChatRoom implements ChatMediator { // the concrete mediator (the tower)
private users: User[] = [];
join(user: User) { this.users.push(user); } // one link per newcomer
send(message: string, from: User) {
// ALL routing logic lives here — colleagues know none of it
for (const u of this.users) {
if (u !== from) u.receive(message, from.name);
}
}
}
class User { // a colleague
constructor(public name: string, private room: ChatMediator) {}
send(message: string) { this.room.send(message, this); } // talk to the hub only
receive(message: string, from: string) {
console.log(`${this.name} got "${message}" from ${from}`);
}
}Trace one message: Ana calls ana.send("hi") → her User object forwards to room.send("hi", ana) → the ChatRoom loops over its member list and calls receive() on everyone but Ana. Ana's class contains no mention of Ben, Chloe, or Dan. Add Eve tomorrow with one room.join(eve) call — Ana's code doesn't change by a single character.
The god-object trap
Mediator's known failure mode: all the interaction logic you removed from the colleagues has to live somewhere, and it all lands in the mediator. Left unchecked, the hub grows into a sprawling god object — thousands of lines that know everything and are terrifying to change. If your mediator is accumulating unrelated coordination rules, split it into several focused mediators, each with one job — the same medicine as [[single-responsibility]]. Simplifying the colleagues by complicating the hub without limit is not a win.
Where you've already met it
- UI dialogs — the GoF's original example. A dialog box coordinates its widgets: when the 'signup' checkbox is ticked, the dialog enables the email field and relabels the button. The checkbox doesn't poke the text field directly; the dialog (mediator) reacts and coordinates.
- Air traffic control — the physical-world mediator. Planes broadcast position and intent to the tower; the tower sequences landings. No pilot-to-pilot negotiation.
- Chat servers — Slack, Discord, IRC. Your client never opens a socket to every other member of a channel; it talks to the server, which routes to the room.
- Event buses / message brokers — components publish events to a bus and subscribe to what they care about, never referencing each other. Pub/sub is Mediator's looser cousin: same hub shape, but the hub routes by topic instead of by bespoke coordination rules.
Mediator vs Observer — the classic mix-up
Both patterns decouple objects that need to react to each other, so they're easy to confuse. [[observer]] is one-to-many broadcast from a subject: one object announces 'I changed', and whoever subscribed gets notified — the subject doesn't know or care what listeners do. Mediator is many-to-many coordination through a hub: a whole group of peers communicate, and the hub owns the rules of the conversation — who hears what, in what order, under what conditions. A handy tell: in Observer the interesting logic lives in the listeners; in Mediator the interesting logic lives in the hub. (In practice a mediator is often implemented using observer-style callbacks — the patterns compose.)
Interactive prototype
See it. Build it. Break it.
A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.
About this simulation
A tiny chat network with four users. Flip between two worlds and watch the connections counter. In WITHOUT Mediator, every user holds a direct link to every other user — 4 people already need 6 tangled lines, and clicking an avatar fires a message down all of its links because the sender must know everyone personally. Press + Add user and Eve joins with a jolt: the mesh jumps from 6 to 10 lines, because every existing user had to learn about her. Flip to WITH Mediator and the tangle collapses: each user keeps exactly one calm line to the central 💬 ChatRoom hub. Click an avatar — one pulse to the hub, and the hub fans it out to the others. Add Eve now and the count ticks from 4 to just 5: one new line, and nobody else changed. The line count is the lesson.
Hands-on
Try these yourself
Open the prototype above, predict what happens, then verify.
Count the tangle
Open the prototype with the toggle on ✕ WITHOUT Mediator. Four avatars — Ana, Ben, Chloe, Dan — are joined by 6 reddish lines, one per pair, and the chip reads connections: 6. Click Ana: pulses race down all three of her direct links at once and each recipient flashes. She could only do that because her object holds a personal reference to every other user.
Feel the cost of growth
Still in the WITHOUT world, press + Add user. Eve joins the circle and the mesh jumps from 6 to 10 lines — the four brand-new links flash red, one for every existing user who had to learn about her. That's the n(n−1)/2 curve biting: user number 5 cost four new connections, and every existing object changed.
Flip to the hub
Switch the toggle to ✓ WITH Mediator. The tangle collapses to 4 calm green lines, each running to the central 💬 ChatRoom hub. Click any avatar: one pulse to the hub, the hub flashes, then it fans the message out to everyone else. Press + Add user again — Eve joins with exactly ONE new line (connections: 4 → 5) and nobody else gained a link. Press ↺ Reset and repeat in both worlds until the counter difference feels obvious.
In practice
When to use it — and what trips people up
Reach for a Mediator when the links themselves are the problem
- Widgets or components with tangled cross-references — a form where the checkbox enables a field, the field validates against a dropdown, the dropdown relabels a button. Move the choreography into one dialog/controller mediator and each widget shrinks back to doing its own job.
- You want to reuse colleagues independently — a class you can't lift out of its context because it names five sibling classes isn't reusable. Route everything through a mediator and the colleague travels light: it only needs a hub, any hub.
- Complex interaction rules that deserve one home — 'when X happens, tell Y unless Z is busy' scattered across ten classes is unfindable. In a mediator, the whole conversation policy sits in one readable place you can test on its own.
- The group's membership changes at runtime — chat rooms, plugin systems, planes entering airspace. With a hub, joining and leaving is one registration call instead of introducing the newcomer to every peer.
When NOT to use it
With only 2–3 objects and simple links, a hub is overkill — you'd be adding an extra class and an extra hop to replace two references that were perfectly readable. Mediator earns its keep when the pair count grows or the interaction rules get intricate. Adding one prematurely just relocates simple code and hands you a future god object; the pattern pays off on the n(n−1)/2 curve, not at n = 3.
What it gives you
- Slashes coupling — n colleagues need n links to the hub instead of up to n(n−1)/2 links to each other, and adding a colleague costs one registration instead of editing every peer.
- Single home for interaction rules — the whole conversation policy ('who hears what, when') lives in one class you can read, test, and change without hunting across the codebase.
- Colleagues become reusable and testable in isolation — a User that only knows 'some mediator' drops into any room or any test with a stub hub; it never names its peers.
- Open to new coordination behavior — swap or subclass the concrete mediator (a moderated room, a logging tower) without touching a single colleague, since they depend only on the mediator interface.
Common mistakes
- God-object risk — every rule you remove from the colleagues lands in the hub, and an unwatched mediator swells into a do-everything class that's scarier than the tangle it replaced.
- Single point of failure and contention — all traffic flows through one object; a bug in the mediator breaks every conversation, and in concurrent systems the hub can bottleneck.
- Indirection tax — following one message now means hopping sender → mediator → receiver, which is harder to trace in a debugger than a direct call.
- Overkill for small groups — with 2–3 simply-linked objects, the extra interface, class, and hop add ceremony without paying back any coupling savings.
Reference
Code & further reading
A minimal reference implementation and pointers worth bookmarking.
// Mediator interface — the only thing colleagues know about
interface ChatMediator {
send(message: string, from: User): void;
}
// Concrete mediator: the hub. ALL routing logic lives here.
class ChatRoom implements ChatMediator {
private users: User[] = [];
join(user: User): void {
this.users.push(user); // a newcomer costs ONE link
}
send(message: string, from: User): void {
for (const u of this.users) {
if (u !== from) { // route to everyone but the sender
u.receive(message, from.name);
}
}
}
}
// Colleague: holds a reference to the mediator — never to other users.
class User {
constructor(public name: string, private room: ChatMediator) {}
send(message: string): void {
this.room.send(message, this); // talk to the hub, not to peers
}
receive(message: string, from: string): void {
console.log(`[${this.name}] ${from}: ${message}`);
}
}
const room = new ChatRoom();
const ana = new User("Ana", room);
const ben = new User("Ben", room);
room.join(ana);
room.join(ben);
ana.send("hello!"); // → [Ben] Ana: hello! (the room routed it)References & further reading
5 sources- Articlerefactoring.guru
Mediator — Refactoring.Guru
The clearest illustrated walkthrough: the tangled-dialog problem, the aircraft/tower analogy, structure diagrams, and code in several languages. Best first read.
- Docsen.wikipedia.org
Mediator pattern — Wikipedia
Concise reference with the formal participant roles (Mediator, Colleague), UML structure, and short examples across languages.
- Booken.wikipedia.org
Design Patterns: Elements of Reusable Object-Oriented Software — Gamma, Helm, Johnson, Vlissides
The original 'Gang of Four' book. Its Mediator chapter uses the classic dialog-box-coordinating-widgets example and discusses the Observer relationship.
- Articlesourcemaking.com
Mediator — SourceMaking
Intent, problem, and checklist framing with the air-traffic-control analogy spelled out, plus common misuse notes.
- Articlepatterns.dev
The Mediator pattern in JavaScript — Addy Osmani, Learning JavaScript Design Patterns
A modern front-end take: chat rooms and event aggregation, and an honest discussion of where Mediator ends and pub/sub begins.
Knowledge check
Did it land?
Quick questions, answers revealed on submit. Sign in to save your best score.
question 01 / 05
What is the core intent of the Mediator pattern?
question 02 / 05
10 objects are fully meshed — each holds a direct link to every other. Roughly how many links exist, and how many would a mediator need?
question 03 / 05
In a ChatRoom mediator, what does a User (colleague) hold a reference to?
question 04 / 05
What is the best-known risk of applying the Mediator pattern?
question 05 / 05
How does Mediator differ from Observer?
0/5 answered