Mediator Pattern
Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly. The foundation of chat rooms, air traffic control, UI form coordination, and event buses.
Why Mediator Exists
You are building a flight booking form. It has many UI components that depend on each other: a departure city dropdown that filters the destination city list, a date picker that disables past dates and triggers a price update, a passenger count spinner that enables/disables the “group discount” checkbox, and a submit button that enables only when all fields are valid. The naive approach: every component knows about every other component directly.
What goes wrong:
- Nร(N-1) coupling — with 6 components, each potentially referencing 5 others, you have up to 30 direct dependencies; a web of spaghetti
- Impossible to reuse —
DepartureDropdowncan’t be used in another form because it hard-codes references toDestinationDropdown,PriceLabel, etc. - Ripple changes — adding a new component (e.g. “loyalty points display”) requires editing every existing component that should interact with it
- Logic scattered — the coordination rules (“when departure changes, update destination and price”) are spread across all components instead of living in one place
- Untestable — you can’t test
DepartureDropdownwithout instantiatingDestinationDropdown,PriceLabel, andSubmitButton
This is the problem Mediator solves — introduce a central coordinator (the mediator) that all components talk to. Components never reference each other directly. They notify the mediator of events, and the mediator decides who else needs to be updated. N×(N-1) links become N links to one hub.
What Is Mediator?
Mediator is a behavioral pattern that defines an object (the mediator) that encapsulates how a set of objects interact. Instead of objects communicating directly with each other (N×N mesh), they communicate only through the mediator (N star). The mediator centralizes complex communication and control logic, promoting loose coupling by keeping colleagues from referring to each other explicitly.
— Gamma, Helm, Johnson, Vlissides (1994)
Key insight: Mediator trades many-to-many for many-to-one-to-many. Instead of N objects each knowing N-1 others, all N objects know only the mediator, and the mediator knows all N. Coupling drops from O(N²) to O(N). The interaction protocol lives in one class (the mediator), not distributed across all participants. This makes interactions easier to understand, change, and test.
mediator.notify(this, "eventName")Participants & Structure
| Participant | Role | In the Analogy |
|---|---|---|
| Mediator (interface) | Declares a method for colleagues to notify it of events (e.g. notify(sender, event)). Defines the communication contract. | The ATC protocol — every pilot must use standard radio calls to communicate with the tower. |
| Concrete Mediator | Implements the coordination logic. Holds references to all colleagues. Receives notifications and orchestrates responses (updates other components). This is where the “business rules” of interaction live. | The actual control tower staff — receives pilot messages, checks all plane positions, issues commands. |
| Colleague (component) | Knows only the Mediator interface. Reports its own state changes via mediator.notify(). Can be updated by the mediator calling its public methods. Never references other colleagues. | A pilot — talks only to the tower, follows tower instructions, doesn’t communicate with other pilots directly. |
- Both decouple senders from receivers.
- The difference: Observer is a one-to-many broadcast — the subject notifies all observers without knowing who they are or what they’ll do.
- Mediator is a centralized coordinator — it receives notifications and makes decisions about which colleagues to update and in what order.
- Observer is “fire and forget”; Mediator is “receive, decide, orchestrate.”
The Pattern In Motion
Scenario: A chat room. Users (Alice, Bob, Charlie) send messages through a ChatRoom mediator. No user holds a reference to any other user — they only know the chat room.
chatRoom.sendMessage(alice, "Hello everyone!")ChatRoom.sendMessage() iterates all users except Alicebob.receive("Alice: Hello everyone!") and charlie.receive("Alice: Hello everyone!"). The mediator decides who receives the message.chatRoom.sendMessage(bob, "Hey Alice!")chatRoom.register(dave)- Every message passes through the hub (mediator).
- Without mediator: Alice must maintain references to Bob, Charlie, Dave (and vice versa) = mesh.
- With mediator: everyone talks to one object = star.
- The mediator can also add logic: filter profanity, log messages, throttle spam — all in one place.
You Already Use This
Mediator appears whenever a central object coordinates interactions between multiple components that shouldn’t know about each other directly.
java.util.Timer — acts as a mediator between multiple TimerTask objects. Tasks don’t know about each other; the Timer schedules, coordinates, and runs them on a shared thread. Adding or removing tasks doesn’t affect other tasks. java.util.concurrent.ExecutorService — mediates between task submitters and worker threads. Submitters don’t know which thread executes their task. The executor coordinates thread allocation, queuing, and lifecycle. javax.swing.ButtonGroup — mediates exclusive selection among radio buttons. When one button is selected, the ButtonGroup deselects all others. Radio buttons don’t reference each other — they only know their group. Spring’s ApplicationContext — acts as a mediator via the event system. Beans publish events to the context; the context routes them to interested listeners. Beans never reference each other directly for event-based communication. Also, Spring MVC DispatcherServlet mediates between controllers, view resolvers, and handler mappings. - In Spring MVC, the
DispatcherServletreceives every HTTP request and mediates between HandlerMappings (who handles it?), Controllers (execute logic), ViewResolvers (which template?), and ExceptionHandlers. - None of these components reference each other — the DispatcherServlet coordinates everything.
- Adding a new controller doesn’t require editing any existing controller or view resolver.
Build It Once
Domain: Chat Room. A ChatMediator interface defines the communication contract. A ChatRoom concrete mediator manages users and routes messages. User objects (colleagues) know only the mediator.
- Since all communication flows through the mediator, you can add profanity filtering, rate limiting, message logging, or “do not disturb” mode in one place — without changing any User code.
- This is impossible when users communicate directly.
Common Mistakes
- If
DepartureDropdownstill holds a reference toDestinationDropdownand calls it directly (bypassing the mediator), you have a hybrid mess — some interactions go through the mediator, some don’t. - Fix: Colleagues must only communicate through the mediator. If a colleague needs another colleague to change, it notifies the mediator, and the mediator calls the other colleague.
- If components just need to broadcast events without coordination logic (“price changed — anyone who cares, update yourself”), Observer is simpler.
- Mediator adds value when the mediator needs to make decisions: conditional routing, ordering, or cross-component validation.
- Don’t over-architect.
- Rule: If the “mediator” just forwards events to all registered listeners without logic — it’s actually Observer dressed up as Mediator.
- If the mediator uses
instanceofchecks or casts to concrete types (if (sender instanceof DepartureDropdown)), it’s tightly coupled to specific implementations. - Fix: Use event types or string identifiers. Colleagues send typed events; the mediator dispatches based on event type, not sender class. Or use the Visitor pattern for type-safe dispatch.
- If your mediator coordinates the booking form AND the user profile AND the search filters, it’s doing too much.
- Fix: One mediator per cohesive subsystem. A
BookingFormMediatorfor the booking form, aSearchMediatorfor search. Keep mediators focused.
When To Use Mediator
- Multiple objects communicate in complex, well-defined ways — the interaction logic is too tangled to live in individual objects
- You have N×N dependencies between components that should be reduced to N×1 — a star topology is cleaner than a mesh
- You need to vary interaction independently of the objects — swap the mediator to change coordination rules without changing components
- Components need to be reusable in different contexts — a
DatePickerthat knows nothing about a booking form can be used anywhere - You want centralized cross-cutting concerns for interactions — logging, validation, or throttling applied in one place
- Components only need simple event broadcast without coordination logic — use Observer instead
- There are only 2-3 components with straightforward interactions — direct calls or Observer are simpler
- The mediator would become a God Object with thousands of lines of business logic — decompose into multiple mediators or use a different architecture
- Interactions are one-directional (one component triggers many) without feedback — Observer is the right fit
| Pattern | Topology | Intelligence | When to pick it |
|---|---|---|---|
| Mediator ← this | Star (hub-and-spoke) | Mediator makes decisions about routing/ordering | Complex bidirectional coordination between many components |
| Observer | Fan-out (1-to-many) | Subject is “dumb” — just notifies all | One event, many independent reactions, no coordination needed |
| Facade | Gateway (simplified API) | Facade orchestrates a subsystem for external clients | Simplify a complex subsystem for outside callers |
| Event Bus | Pub/Sub (topic-based) | Bus is a dumb pipe — routes by topic, no decisions | Loosely coupled events across modules without coordination |
Problems To Solve
Mediator problems test whether you can centralize complex coordination logic, decouple interacting components, and keep the mediator thin and focused.
| Difficulty | Problem | Key Insight |
|---|---|---|
| Easy | Chat Room Build a chat room where users send messages through a mediator. Users can join and leave. Messages from one user are delivered to all others. Users never hold references to other users. Add a “mute” feature: muted users can send but their messages aren’t delivered. | Tests the basic Mediator structure. The chat room (mediator) holds a list of users and iterates to deliver. The “mute” feature demonstrates centralized logic: one line in the mediator suppresses delivery without changing User code. |
| Medium | Smart Home Controller Build a smart home system. Devices: MotionSensor, LightSwitch, Thermostat, SecurityCamera, DoorLock. Rules: motion detected at night โ lights on + camera recording; temperature > 30°C โ AC on; all motion stopped for 30min โ lights off + door locked. Devices don’t know about each other — the HomeMediator coordinates. | Tests conditional coordination logic. The mediator receives notify(sensor, "motion") and applies rules (checks time, checks other device states) before issuing commands to other devices. This shows why Mediator > Observer: the mediator needs to inspect context (time of day) and state of other devices before acting. |
| Medium | Form Validation Coordinator Build a login/registration form with fields: EmailField, PasswordField, ConfirmPasswordField, SubmitButton, ErrorDisplay. Rules: submit enabled only when all fields valid; confirm must match password; email must be unique (async check); error display shows the first failing field. Fields notify the mediator on change; mediator orchestrates validation and enables/disables submit. | Tests real-world UI mediation. The mediator receives “field changed” events, runs cross-field validation (confirm == password), and updates error display + submit button. Fields are reusable โ PasswordField doesn’t know about ConfirmPasswordField. The mediator handles the async email uniqueness check and coordinates the result. |
| Hard | Air Traffic Control System Build an ATC system. Aircraft have: position, altitude, heading, speed. The ControlTower mediator enforces: minimum 5km horizontal separation; no two aircraft on the same runway; landing queue (FIFO); emergency priority. Aircraft call tower.requestLanding(this); the tower checks all other aircraft positions and either grants clearance or puts them in a holding pattern. Log all decisions. | Tests Mediator at scale with complex state-based decisions. The tower must inspect positions of ALL aircraft to grant one request (true centralized intelligence). Emergency priority means the mediator can reorder the queue. Logging of decisions demonstrates auditability. This is the original GoF motivation for Mediator โ the complexity belongs in ONE place, not distributed across N aircraft. |
- When asked about Mediator, the interviewer wants to see: (1) a
Mediatorinterface withnotify(sender, event); (2) a concrete mediator with coordination logic (not just forwarding); (3) colleagues that know only the mediator interface, never each other; (4) clear explanation of why it’s not Observer (“Mediator makes decisions; Observer just broadcasts”). - Stand-out answers mention:
DispatcherServletas the canonical Java Mediator, the God Object risk (keep mediator thin), star vs. mesh topology (N vs. - N² coupling), and when not to use it (simple broadcast โ Observer; few components โ direct calls).