Define an object that encapsulates how a set of objects interact — promoting loose coupling by keeping objects from referring to each other explicitly.
The Mediator pattern introduces a central object (the mediator) that all other objects communicate through, instead of communicating directly with each other. Objects become loosely coupled — they only know about the mediator, not each other.
Think of an Air Traffic Controller: planes don't talk to each other directly — they'd create chaos. They all communicate through the control tower (mediator), which coordinates landings, avoids collisions, and manages the runway queue.
This reduces the number of relationships from O(n²) (every object to every object) to O(n) (every object to the mediator).
Key Idea: Instead of many-to-many communication between objects, route all communication through one mediator. Reduces coupling from O(n²) to O(n).
Declares the communication interface between colleagues. Usually a notify(sender, event) method that colleagues call to inform the mediator of state changes.
Implements the coordination logic. Knows all colleagues and decides how to react to events. All the complex inter-object logic lives here — not in the colleagues.
Any object that communicates through the mediator. Each colleague knows only about the mediator interface — never about other colleagues directly. This is the key decoupling.
Without Mediator, every component directly references the others it needs to coordinate with. As the system grows, every new component adds references to every other component.
// Without Mediator — components reference each other directly
class CityDropdown {
constructor(countryDropdown, stateDropdown, submitBtn) {
this.country = countryDropdown; // tight coupling
this.state = stateDropdown; // tight coupling
this.submit = submitBtn; // tight coupling
}
onChange() {
this.state.updateOptions(this.country.getValue());
this.submit.checkValid();
}
}
// Every component is coupled to every other. A web of dependencies.
// With Mediator — components only know the mediator
class CityDropdown {
onChange() { this.mediator.notify('city', 'changed', this.value); }
}| Aspect | Mediator | Observer |
|---|---|---|
| Communication | Many ↔ mediator ↔ many | One subject → many observers |
| Logic lives in | The mediator | Each observer |
| Direction | Bidirectional coordination | One-way broadcast |
| Awareness | Mediator knows all colleagues | Subject knows observers exist, not details |
// Mediator Pattern — Air Traffic Control
// ─── Mediator interface ───────────────────────────────────────
interface AirTrafficControl {
void requestLanding(Aircraft aircraft);
void notifyLanded(Aircraft aircraft);
}
// ─── Colleague ────────────────────────────────────────────────
abstract class Aircraft {
protected AirTrafficControl atc;
protected String flightId;
public Aircraft(String flightId, AirTrafficControl atc) {
this.flightId = flightId;
this.atc = atc;
}
public String getFlightId() { return flightId; }
public abstract void land();
public abstract void waitForClearance();
}
// ─── Concrete Colleagues ──────────────────────────────────────
class CommercialFlight extends Aircraft {
public CommercialFlight(String id, AirTrafficControl atc) { super(id, atc); }
public void land() {
System.out.println("[" + flightId + "] Landing on runway...");
atc.notifyLanded(this);
}
public void waitForClearance() {
System.out.println("[" + flightId + "] Holding pattern, awaiting clearance...");
}
public void requestLand() { atc.requestLanding(this); }
}
// ─── Concrete Mediator ────────────────────────────────────────
class RunwayController implements AirTrafficControl {
private boolean runwayFree = true;
private java.util.Queue<Aircraft> queue = new java.util.LinkedList<>();
@Override
public void requestLanding(Aircraft aircraft) {
if (runwayFree) {
runwayFree = false;
System.out.println("[ATC] Cleared " + aircraft.getFlightId() + " to land");
aircraft.land();
} else {
System.out.println("[ATC] Runway busy — " + aircraft.getFlightId() + " must wait");
aircraft.waitForClearance();
queue.add(aircraft);
}
}
@Override
public void notifyLanded(Aircraft aircraft) {
System.out.println("[ATC] " + aircraft.getFlightId() + " has landed. Runway clear.");
if (!queue.isEmpty()) {
Aircraft next = queue.poll();
System.out.println("[ATC] Clearing " + next.getFlightId() + " from queue");
next.land();
} else {
runwayFree = true;
}
}
}
// ─── Client ───────────────────────────────────────────────────
public class App {
public static void main(String[] args) {
RunwayController atc = new RunwayController();
CommercialFlight ai101 = new CommercialFlight("AI-101", atc);
CommercialFlight ba202 = new CommercialFlight("BA-202", atc);
CommercialFlight ek303 = new CommercialFlight("EK-303", atc);
ai101.requestLand(); // lands immediately
ba202.requestLand(); // queued
ek303.requestLand(); // queued — BA-202 lands next automatically
}
}When form fields depend on each other — country changes affect city options, shipment type shows/hides fields, validation spans multiple components.
FormMediator, DialogMediator, WizardMediatorWhen multiple users/components need to communicate — route all messages through a chat room mediator rather than peer-to-peer.
ChatRoom, EventBus, MessageBrokerWhen multiple agents compete for shared resources — the mediator serialises access and manages queuing.
RunwayController, PrintSpooler, TaskSchedulerWhen game objects (players, enemies, environment) need to interact — route events through a game event mediator instead of direct references.
GameEventBus, CollisionMediatorThe Redux store is a Mediator — all components dispatch actions to it, and it coordinates state updates and notifies the right subscribers. Components never talk to each other directly.
Spring's ApplicationEventPublisher lets beans publish events and other beans subscribe. The Application Context acts as the mediator — publishers and subscribers never reference each other.
Subjects in RxJS act as mediators — multiple producers push values to a Subject; multiple consumers subscribe without knowing about the producers.
Message brokers are infrastructure-level mediators. Producers and consumers never communicate directly — all communication is routed through topics/queues in the broker.