Mediator
Ban components from talking to each other directly and route every interaction through one mediator object, so nobody depends on anybody except the hub.
The problem
You have a dialog for editing customer profiles: text fields, checkboxes, buttons. Some elements react to others. Ticking "I have a dog" reveals a field for the dog's name. The submit button has to validate every field before saving.
Put that logic inside the elements and the elements stop being elements. Your checkbox class now holds a reference to a specific text field, so it cannot appear in any other form. You can reuse the entire profile screen or none of it.
Each new relationship is another wire in a growing cat's cradle, and every wire is a reason a class cannot move.
The solution
Cut all direct communication between the components you want independent. They now collaborate indirectly, by calling one mediator object that redirects to whoever should react. Each component depends on a single class instead of a dozen colleagues.
In the profile form the dialog itself makes a natural mediator, since it already knows about its own children. The change lands on the components. The submit button used to validate every field; now its whole job is to tell the dialog it was clicked. The dialog does the validating, or delegates it.
Then extract a mediator interface with the notification method. Now the button works with any dialog implementing it, and the component becomes genuinely portable. Fewer dependencies per class, easier to change, extend, and reuse.
- 1The user flips the "I want to register" checkbox. So far, an ordinary widget doing an ordinary thing.
- 2The checkbox reports the event and its own identity to the mediator. That is the entirety of its social life.
- 3The mediator identifies the sender and looks up the rule. All the coupling that used to be spread across widgets sits in this one method.
- 4The mediator reveals the registration fields and hides the login ones. The checkbox never learned that these fields exist.
- 5The user hits OK. The button does not validate anything - it has no references with which to validate.
- 6Same single channel, different sender. The mediator now knows it is in registration mode and which fields to read.
- 7The mediator orchestrates the finish. Reuse this Button in a settings form tomorrow: give it a different mediator, change nothing else.
Structure
The important constraint is negative: components must not know about other components. When something happens, a component notifies the mediator and nothing else. From inside a component, the rest of the system is an opaque box - the sender does not know who will handle its news, and the receiver does not know who caused it.
Code
Same example three ways: an authentication dialog that flips between login and registration, acting as its own mediator.
When to use it
- Some classes are painful to change because they are welded to a crowd of other classes. Mediator lifts the relationships out into one place, so a change to one component stops rippling.
- A component cannot be reused elsewhere because it depends on its neighbors. Give it a new mediator instead of new neighbors.
- You are breeding component subclasses purely to reuse basic behavior in different contexts. New collaboration rules should mean a new mediator, not new components.
Pitfalls
- The God Object. Every rule you remove from a component lands in the mediator. Left unchecked it becomes the class nobody dares edit. Split by concern before that happens.
- Coupling relocated, not deleted. The system is still exactly as interconnected; you have just made the connections visible and central. That is usually worth it - but do not mistake it for simplification.
- Anemic components. Push too hard and widgets become dumb event emitters with no behavior worth naming, and every trivial interaction takes a round trip through the hub.
- Stringly typed events.
notify(sender, "chekc")compiles fine and does nothing. Enums or typed events cost little and catch this at build time.
Don't confuse it with
- Observer. The elusive one. Mediator's goal is eliminating mutual dependencies among peers by making them all depend on a hub; Observer's goal is dynamic one-way subscriptions where some objects are subordinate to others. They blur because a popular Mediator implementation uses Observer internally - the hub is the publisher, the components are subscribers. But you can also permanently wire every component to one mediator, which looks nothing like Observer and is still Mediator. Push the other direction, make every component a publisher with dynamic links, and you have no mediator at all - just a distributed mesh of observers.
- Facade. Both organize collaboration among tightly coupled classes. A Facade offers a simplified interface to a subsystem without adding functionality; the subsystem does not know the facade exists and its objects still call each other directly. A Mediator centralizes communication and its components have no other channel.
- Command. Command keeps the sender-to-receiver link and packages it as an object. Mediator deletes the link outright.
- Chain of Responsibility. CoR arranges receivers in a line the request travels along; Mediator puts one object in the middle that every message must pass through.
Check yourself
After applying Mediator, what does a Button know about the rest of the form?