The Mediator pattern introduces a central coordinator for communication between objects. Instead of a many-to-many web of references, components communicate through a defined hub. This hub acts as a single point of contact, so each component only needs to know one interface.
The Problem
A checkout page may contain a cart, shipping form, payment form, summary, and coupon field. If every component calls every other component, adding or changing one feature spreads through the whole page. This can lead to a tangled web of dependencies, making the system harder to manage and maintain. When a single change forces many modules to be updated, the likelihood of bugs rises and testing becomes more labor‑intensive.
The Mediator Solution
interface CheckoutMediator {
onShippingChanged(method: ShippingMethod): void;
onCouponApplied(coupon: Coupon): void;
}
class ShippingForm {
constructor(private mediator: CheckoutMediator) {}
select(method: ShippingMethod) {
this.mediator.onShippingChanged(method);
}
}
class CheckoutCoordinator implements CheckoutMediator {
onShippingChanged(method: ShippingMethod) {
// update cart, payment, summary, and eligibility in one place
}
onCouponApplied(coupon: Coupon) {
// coordinate the related updates
}
}
Components know the mediator, not each other. The mediator defines the protocol and coordinates the workflow, ensuring that changes in one component are properly propagated to others without direct dependencies. When the shipping form calls onShippingChanged, the mediator decides which parts of the cart, payment module, and summary need updating, and invokes them in a controlled order.
Examples
A chat room mediates messages between users, ensuring that messages are routed correctly and notifications are managed centrally. A control tower coordinates aircraft movements, ensuring safe and efficient air traffic control. A form mediator updates dependent fields when a country or address changes, maintaining consistency across the form. In each case, the example illustrates a coordination model, not a claim that every real system uses one class. The pattern shows how a single object can enforce business rules, such as preventing a user from sending a message to a blocked participant, without each participant needing to know the others.
Advantages and Risks
A mediator can reduce coupling, centralise interaction rules, and make components easier to test in isolation. However, the main risk is the creation of a god object that accumulates validation and business logic. This can turn the mediator into a complex entity that is difficult to manage. To avoid this, keep domain rules in domain services or components; let the mediator handle coordination tasks only. Domain services encapsulate the core business invariants, while the mediator simply orchestrates calls, preserving a clear separation of concerns. The extra indirection introduced by the mediator is not without cost. Direct calls are often clearer for simple, stable interactions where the mediator's benefits are less pronounced. If a button only toggles a single flag, calling the flag setter directly avoids the overhead of an extra object.
Mediator vs Event Bus
| Aspect | Mediator | Event bus |
|---|---|---|
| Protocol | Explicit methods | Topics and subscriptions |
| Traceability | Usually direct | Can be harder to follow |
| Coupling | Components know mediator | Publishers and subscribers are loosely coupled |
| Best fit | Coordinated workflows | Broad event distribution |
When to Use It
Use a mediator when several components coordinate complex state and direct references are becoming difficult to maintain. Avoid it when components are independent or the mediator would need to know about almost everything.
The Bottom Line
Mediator replaces tangled peer-to-peer dependencies with a hub. When kept small and explicit, it can significantly improve readability and testing. However, let it become a dumping ground for various functionalities, complicating the system rather than simplifying it.

Comments (0)
Please sign in to leave a comment