| Lesson 12 | Mediator: consequences |
| Objective | Evaluate the benefits, trade-offs, and implementation risks of the Mediator pattern. |
The Mediator pattern is a behavioral design pattern that encapsulates how a defined group of objects collaborate. Participating objects communicate through a mediator instead of embedding concrete knowledge of every peer. This changes the dependency graph and gives cross-object policy a deliberate home. The classic Gang of Four name for a participant is colleague, although production code usually uses domain names such as control, device, component, participant, or service.
This rearrangement is a trade-off, not the disappearance of complexity. Colleagues often become smaller and more reusable, while the concrete mediator becomes more knowledgeable. A successful mediator keeps that knowledge bounded and testable. A poor mediator becomes an opaque coordinator that is difficult to change, diagnose, or recover.
The pattern's primary benefit is reduced concrete coupling among participating objects. Coupling is the degree to which one component depends on another component's interface, implementation, state, or lifecycle. In a direct design, one colleague may store several peer references, call concrete methods, and duplicate assumptions about when those methods should run. Changes to a peer or to an interaction rule can then spread across many classes.
With Mediator, each colleague normally depends on a mediator contract or narrow communication port. The concrete mediator depends on the colleague operations required to coordinate the workflow. Dependencies have not vanished. Many peer dependencies have been replaced by fewer intentional dependencies whose direction and purpose are easier to inspect. This is loose coupling: limiting knowledge so that a component can change without forcing unrelated components to change.
A mediator can improve replaceability, but it does not automatically create a plugin architecture. A replacement must satisfy the mediator-facing contract, without violating hidden assumptions. Construction, ownership, registration, and removal must be explicit. Workflow state must remain valid, and collaboration tests must verify the new composition.
| Design concern | Potential benefit | Associated cost |
|---|---|---|
| Dependencies | Fewer concrete peer references | Colleagues depend on the mediator contract, and the mediator knows the collaboration |
| Interaction rules | Policy is localized | The mediator can grow as rules accumulate |
| Testing | Colleagues and coordination can be isolated | Integrated workflow tests remain necessary |
| Variation | Coordination can change independently | Compatible contracts and explicit composition are required |
| Operations | One place supports tracing and metrics | A synchronous central path can become a bottleneck or failure boundary |
Every benefit brings a related concentration of responsibility. This is not an argument against Mediator. It identifies what must be controlled for the pattern to provide useful decoupling without merely relocating disorder.
A concrete mediator can become a god object when it accumulates unrelated workflows, domain calculations, persistence, presentation, security, and infrastructure concerns. Warning signs include many reasons to change, a growing dispatch method, application-wide dependencies, fragile call-order tests, untyped event strings, nested callbacks, and behavior developers fear modifying.
A mediator must know enough about participant capabilities to coordinate them. That necessary knowledge becomes feature envy when it reaches deeply into colleagues' internal state, duplicates their invariants, or performs behavior that belongs to them. Prefer meaningful operations such as signal.showStop() or form.displayErrors(errors) over direct manipulation of internal fields.
Keep the mediator bounded by a cohesive workflow, screen, device group, or conversation. Split unrelated coordination. Return calculations and invariants to their domain objects or services. Use typed commands, events, or domain operations; extract routing, validation, scheduling, and persistence into focused collaborators; and model substantial rules as explicit states or transitions.
Do not respond to every variation by subclassing an increasingly large mediator. Inheritance can conceal shared state and sequencing assumptions. Composition and replaceable policies are usually clearer when behaviors vary independently.
An in-process mediator is central to a collaboration, but it is not automatically a system-wide single point of failure. Its exception may interrupt one workflow without destroying every colleague. A remote mediation service, however, can become a real availability dependency. The risk depends on scope, deployment, and failure policy.
Safeguards include validation, explicit error results, exception boundaries, and partial-failure tests. Retry only safe, idempotent work. Distributed mediators may also need timeouts, fallback behavior, redundancy, and delivery guarantees.
A synchronous mediator on every high-volume operation can become a hot path. Excessive locking, blocking I/O, database access, or unnecessary broadcasting causes the cost, not the pattern name. Measure before adding caches, queues, partitioning, or concurrency.
One event may cause the mediator to change several other objects, so effects are less obvious at the initiating call site. Meaningful method names, typed events, structured tracing, focused mediator methods, and concise interaction documentation make the path visible.
A mediator action may trigger another colleague event before the first operation finishes. Without a policy, cascading updates can duplicate work, expose inconsistent intermediate state, or create an infinite cycle. Safeguards include ignoring unchanged values, distinguishing commands from notifications, queueing nested events, tracking the current transition, or using a state machine.
Define whether colleague updates must occur in a particular order and what happens after partial completion. Deterministic sequencing may be sufficient in a user interface. Database or distributed work can require transaction boundaries, compensation, idempotency, or eventual consistency. The GoF object pattern does not provide distributed transactions.
If several threads call the mediator, mutable workflow state and colleague references need an explicit concurrency policy. Thread confinement, immutable messages, serialized processing, narrowly scoped locks, or actors are possible choices. Synchronization should follow the actual execution model rather than being added mechanically.
A mediator that registers dynamic colleagues must define ownership and removal. Forgotten registrations can retain objects or route events to inactive participants. Use scoped ownership, explicit deregistration, and tests for replacement and shutdown. Use weak references only when their lifecycle semantics fit.
Centralizing collaboration policy does not require a singleton or universal coordinator. An application can contain many small mediators for forms, intersections, workflows, conversations, or device groups. In a graphical user interface, a form mediator can coordinate validation and enabled states without every control knowing every other control.
Centralized is a statement about responsibility within a collaboration, not necessarily physical deployment. Logical mediation can be partitioned, replicated, or implemented through messaging, but distributed designs introduce consistency, ordering, and delivery concerns beyond the classic GoF pattern.
In a direct design, interaction knowledge is distributed across colleagues. Each class may appear simple until its peer references and callbacks are traced. In a mediated design, colleagues expose focused capabilities and report meaningful events, while the mediator makes their collaboration policy visible in one bounded location. The workflow may remain complex, but its complexity is represented explicitly instead of repeated across many participants.
A fully connected group of n colleagues can have approximately n(n - 1) / 2 pairwise relationships. Mediation can replace many of them with roughly one mediator relationship per colleague. This illustrates graph simplification; it does not predict source-line count, runtime performance, or maintenance cost.
A direct call is often the clearer choice for a stable relationship between two objects. Do not introduce a mediator merely to remove every object reference. Use it when multi-object coordination policy is the changing, duplicated, or difficult part of the design. A mediator that only forwards a call from one object to another adds indirection without encapsulating meaningful policy.
Unit-test each colleague's local behavior and the meaningful events it reports to a fake mediator. Test the concrete mediator's decisions with focused fakes for colleague capabilities. Cover state transitions, invalid requests, duplicates, ordering, nested events, and removal. Add collaboration tests using the real wired participants, plus applicable failure tests for exceptions, timeouts, or partial completion. Reserve load and concurrency tests for performance-sensitive or multithreaded paths.
Avoid mock tests that assert every internal call. Prefer observable state changes, meaningful commands, and declared ordering requirements so that safe internal refactoring does not break tests. When operations depend on logs or metrics, verify correlation and error classification without coupling tests to unstable message wording.
| Favors Mediator | Favors a simpler design |
|---|---|
| Several colleagues participate in changing interaction rules | Only two objects have a stable, natural relationship |
| Coordination logic is duplicated across components | A direct method call clearly expresses the dependency |
| Components must vary independently of the workflow | The proposed mediator would only forward calls |
| Sequencing, validation, or routing needs one cohesive policy | The interaction contains no meaningful coordination policy |
| Collaboration-wide testing and tracing are valuable | Added indirection would obscure a simple operation |
Mediator succeeds when it converts an unstable web of peer knowledge into a clear, bounded collaboration policy. Its value is not that it removes complexity, but that it places interaction complexity where it can be understood, tested, changed, and monitored without overwhelming the participating objects.