codejump

AcademyDesign Patterns

Mediator pattern

Routing communication between many objects through one hub, the god object the hub itself can become, and the temptation to route every interaction through it "to be safe."

5 min readUpdated

3 exercises

Waiting at the end of this page.

Mediator is for many-to-many communication: instead of every object holding references to every other object it needs to talk to, all of them talk through one shared go-between.

A Mediator adds a third object that every participant talks through, instead of participants holding direct references to each other. Where Observer lets many listeners find out about one subject, Mediator lets many peers talk to each other without any pair of them knowing the other exists.

The problem it solves

A form has a country select, a state select, a zip code field and a submit button. Picking a country changes which states are available, changes the zip code format, and enables or disables the button.

If each field talks to the others directly, every field needs a reference to several others:

  • Connections multiply. Four components can have twelve one-way links. Add a fifth and it needs links to all four.
  • Each component knows too much. The country select knows how the zip field validates, so it cannot be reused in another form.
  • Changing a rule touches many files. "Some countries have no states" means editing three components.

The fix is to move the coordination into one object. Components report what happened to it and never talk to each other.

Structure

Participants only know the mediator. It receives each message and decides who gets it
Participants only know the mediator. It receives each message and decides who gets it

Three parts:

  • The mediator interface: how participants report events.
  • The concrete mediator (ChatRoom): holds references to the participants and contains the coordination rules.
  • Participants (User): each holds a reference to the mediator, not to each other.
class ChatRoom {
  showMessage(user, message) {
    return user.name + ': ' + message;
  }
}

class User {
  constructor(name, chatRoom) {
    this.name = name;
    this.chatRoom = chatRoom;
  }
  send(message) {
    return this.chatRoom.showMessage(this, message);
  }
}

// const room = new ChatRoom();
// const alice = new User('Alice', room);
// const bob = new User('Bob', room);
// alice.send('hi bob') → 'Alice: hi bob'

Alice sends a message without knowing who is in the room. The room decides who gets it, and could later filter, log or reject it, without any user changing.

Where it is used in real systems

Form fields report changes to one controller, which knows how they depend on each other
Form fields report changes to one controller, which knows how they depend on each other
Use caseThe mediator
Chat roomsDelivers messages between users without them holding each other's addresses.
Form coordinationOne controller deciding how fields affect each other.
Air traffic controlPlanes talk to the tower, never to each other.
UI dialogsA dialog controller enabling buttons based on several inputs.
Event busesA shared channel that components publish to and read from.
State storesRedux and Pinia: components dispatch and read, and never call each other.
Microservice orchestrationA workflow service coordinating services that do not know about each other.

Mediators you already use

  • Redux and Pinia stores. Components do not call each other. They dispatch to a store, and the store decides what changes.
  • Form libraries. React Hook Form and Angular's FormGroup coordinate several controls and their validation.
  • Message brokers. Kafka, RabbitMQ and SQS sit between services that do not know about each other.
  • Air traffic control is the textbook example, and comes up in interviews.
  • Angular services and Vue provide/inject used as a shared coordinator between sibling components.

Mediator or Observer

Both decouple objects and are easy to confuse.

ObserverMediator
DirectionOne subject to many listenersMany peers, through one hub
Who knows whatSubject knows its listeners existPeers know only the mediator
LogicIn each listenerCentralized in the mediator
CommunicationBroadcastRouted, and can be filtered or transformed

An event bus is often both: the observer pattern used to implement a mediator.

When not to use it

  • It turns into a god object. All the coordination logic piles into one class, and the mediator becomes the hardest thing to change. Split it by feature.
  • Two objects that talk to each other. A mediator between two components is just an extra hop.
  • The relationships are simple. A parent that passes props and callbacks to a child does not need a mediator.
  • It hides the flow. With everything routed through one place, it can be hard to see who triggers what. Keep the mediator's rules readable and tested.

What interviewers ask

  • "What is the Mediator pattern?" A central object coordinating communication between others, so they do not reference each other.
  • "Mediator versus Observer?" Observer broadcasts from one subject. Mediator routes between many peers and holds the coordination logic.
  • "What is the downside?" The mediator can become a bloated god object.
  • "Give a real example." A chat room, a form controller, or a Redux store.
  • "Mediator versus Facade?" A Facade simplifies access to a subsystem and the subsystem does not know about it. A mediator is known to its participants, and they communicate through it.

The mistake to avoid

A Mediator that participants also bypass. If User objects sometimes call each other directly, the coordination rules in the room no longer apply to every message, and you have both the coupling and the extra hop. Make the mediator the only path, or do not use one.

The mechanism above, participants that hold a reference to one shared coordinator, is the same in JavaScript, Python or Java. Only the syntax changes.

Now practice it

1 challenge · 2 quizzes

Reading this page is the cheap half. These are the exercises that make you use it.