LESSON 6.2
Mediator
Define an object that encapsulates how a set of objects interact, promoting loose coupling by preventing them from referring to each other explicitly.
15 min read
Real-world analogy
An airport air traffic control tower (ATC) is a classic mediator. When twenty airplanes arrive at Heathrow Airport simultaneously, pilots do not radio each other in a chaotic mesh of dozens of simultaneous conversations to negotiate who lands first. Instead, every flight communicates exclusively with the air traffic control tower. The tower coordinates runway clearances, flight altitudes, and holding patterns, preventing mid-air collisions through centralized coordination.
The problem
You are building an authentication and checkout dialog box. The dialog contains several interdependent interactive UI widgets:
Checkbox: "Create new account" toggle.TextBox: "Password confirmation" input.Dropdown: "Account Type" (Personal vs Corporate).Button: "Submit" action button.
The interaction rules are complex:
- When "Create new account" is unchecked, the password confirmation input and account type dropdown must be disabled and cleared.
- When "Account Type" is Corporate, a tax identification input must appear.
- The "Submit" button must remain disabled until all currently active required fields are valid.
Without a Mediator, every widget must hold direct references to every other widget, creating an unmaintainable $N(N-1)/2$ mesh of tight coupling:
// Naive approach: Tight mesh of cross-references among peer widgets
class Checkbox {
#passwordBox;
#accountDropdown;
#submitButton;
// Tangled mesh of references
setPeers(passwordBox, accountDropdown, submitButton) {
this.#passwordBox = passwordBox;
this.#accountDropdown = accountDropdown;
this.#submitButton = submitButton;
}
onToggle(isChecked) {
if (!isChecked) {
this.#passwordBox.disable();
this.#accountDropdown.disable();
} else {
this.#passwordBox.enable();
this.#accountDropdown.enable();
}
this.#submitButton.validateState();
}
}public class Example {
static class Checkbox {
// High coupling: Checkbox must know concrete details of TextBox, Dropdown, and Button
public void onToggle(boolean isChecked) {
System.out.println("Manually mutating 4 peer widgets directly...");
}
}
public static void main(String[] args) {}
Adding a fifth widget (e.g. "Remember Me" or "Two-Factor Auth") forces you to update the event handlers and reference fields in all existing widgets. None of the widgets can be reused in another dialog because they are hardwired to their peers.
The solution
The Mediator pattern encapsulates how a set of objects interact. Instead of communicating directly with each other, colleagues communicate only with their shared Mediator. The mediator orchestrates the interactions and business rules in one place.
- Mediator (
DialogMediator): Interface declaring notification contracts (notify(sender, event)). - Concrete Mediator (
AuthDialog): Implements coordination logic among all widgets. - Colleague (
UIComponent): Base class holding a reference to the mediator. - Concrete Colleagues (
Button,TextBox,Checkbox): Dispatches events to the mediator without knowing which other widgets exist.
Step-by-step implementation
Step 1: Define the mediator interface
Declare the communication method through which colleagues report events.
class Mediator {
notify(sender, event) {
throw new Error("notify() must be implemented");
}
}public class Example {
interface Mediator {
void notify(Component sender, String event);
}
Step 2: Define the colleague base class
Colleagues store a mediator reference and delegate notifications to it upon user interaction.
Step 3: Implement concrete colleagues
Concrete widgets know only their own local state and emit events.
The complete example
Here is the complete, runnable implementation demonstrating an air traffic control tower coordinating runway landings.
[BA-100] Requesting clearance to land...
[Tower] Runway 09L CLEARED for BA-100.
[BA-100] Touchdown confirmed on Runway 09L. Taxiing off.
[Tower] Runway 09L is now VACANT.
[AA-200] Requesting clearance to land...
[Tower] Runway 09L CLEARED for AA-200.
[AA-200] Touchdown confirmed on Runway 09L. Taxiing off.
[Tower] Runway 09L is now VACANT.
[LH-300] Requesting clearance to land...
[Tower] Runway 09L CLEARED for LH-300.
[LH-300] Touchdown confirmed on Runway 09L. Taxiing off.
[Tower] Runway 09L is now VACANT.Variations
Chat room vs air-traffic tower
- Air Traffic Tower (Coordination & Allocation): The mediator acts as an arbiter that manages shared, exclusive physical resources (e.g. runways, thread pools, locks), keeping queues and directing when colleagues can proceed.
- Chat Room (Broadcast Routing): The mediator acts as a message router where users send messages to a room, and the room rebroadcasts the message to all participants in that room while handling block lists and permissions.
Mediator vs observer
Engineers frequently combine or confuse Mediator and Observer:
- Topology: Observer creates a one-to-many publisher-subscriber broadcast. Mediator creates a many-to-many peer coordination hub.
- Direction: Observer notifications are generally unidirectional. Mediator interactions are bidirectional (colleagues send events to the mediator, and the mediator directs colleagues to change state).
- Synergy: In modern GUI architectures, colleagues often publish events to the mediator using the Observer pattern. The mediator registers as an observer on all colleague widgets!
The "God Mediator" risk
Because all colleague interactions flow through the mediator, the mediator class naturally attracts validation rules, routing heuristics, calculations, and error-handling branches. Over time, it risks degenerating into an unmaintainable monolithic God Object. To avoid this:
- Keep domain validation inside colleagues or dedicated service objects.
- Split large dialog mediators into composite sub-mediators.
Where you've already used it
- Redux / Vuex Store: UI components dispatch actions to a single global Store. Reducers process the actions and update the central state, acting as a mediator for all component views.
- Java Swing
ButtonGroup: Coordinates a set ofJRadioButtoncomponents, ensuring that clicking one button unselects all other buttons in the group. - Spring MVC
DispatcherServlet: Acts as the central mediator for HTTP requests, coordinating handler mappings, interceptors, controllers, and view resolvers. - Socket.io Chat Rooms: Rooms manage connected socket instances, routing broadcast messages to designated channel subsets.
Pros and cons
Advantages
- Decoupled Colleagues: Individual components know only their own local state and the mediator interface, making them reusable.
- Centralized Interaction Rules: Communication policies, validation sequences, and mutual exclusions live in a single testable class.
- Reduced Graph Complexity: Replaces $O(N^2)$ direct connections with $O(N)$ star connections.
Disadvantages
- God Object Anti-Pattern: The mediator can easily balloon into an unwieldy, highly coupled monolithic class.
- Single Point of Failure: Logic errors in the mediator break communication among all connected colleagues.
When not to use it
Do not use Mediator when you only have two or three components with straightforward interactions. Directly connecting two components via an observer callback or strategy is much simpler than creating an abstract mediator infrastructure.
Related patterns
| Pattern | Topology | Primary Focus |
|---|---|---|
| Mediator | Many-to-many | Coordinates complex interactions among peers |
| Observer | One-to-many | Broadcasts state changes to subscribers |
| Facade | Unidirectional | Simplifies an entire subsystem for external callers |
| Command | One-to-one | Encapsulates a request as an object |
Mediator: chat room coordinator
Decouple chat participants from holding direct peer-to-peer references by coordinating messaging through a central ChatRoom mediator.
Click Run tests to evaluate your solution against the behavior and structural test suite.
Interview questions
How does Mediator eliminate class coupling? Instead of $N$ colleague classes holding references to each other (requiring up to $N(N-1)/2$ couplings), each colleague holds a reference only to the Mediator interface ($N$ couplings).
What is the difference between Mediator and Facade? Facade provides a unidirectional simplified interface to a subsystem without the subsystem classes knowing about the facade. Mediator coordinates bidirectional peer-to-peer communication where colleagues actively notify and respond to the mediator.
How do you prevent a Mediator from becoming an unmaintainable God Object? Delegate actual business rules, validation, and domain logic to specialized domain service classes. The mediator should only orchestrate the control flow and routing between components, not execute complex business calculations.
Can Observer and Mediator be used together? Yes. In fact, they are often paired: Colleagues act as subjects that emit events using the Observer pattern. The Mediator registers as the single observer on all colleagues, coordinating updates without hardwiring callbacks.
Try it
In-process Event Bus LLD
Define subscription, dispatch and error semantics for a reusable event dispatcher.