Mediator
Behavioral Design Patterns
Imagine you are creating a user registration dialog box with several interacting UI components:
- A
Username
textbox. - A
Password
field. - A
Terms & Conditions
checkbox. - A
Register
button.
These components have complex dependencies. The Register
button should be disabled until the Terms & Conditions
checkbox is checked. The Username
and Password
fields might need to show an error icon if validation fails after the Register
button is clicked.
In a naive design, each component would need a direct reference to all the other components it interacts with. The Checkbox
would need to know about the Button
to enable it. The Button
would need to know about the TextBox
and Password
field to trigger their validation. This creates a tangled "rat's nest" of dependencies where every object is tightly coupled to several others. Adding a new component, like a "confirm password" field, would require updating many existing classes.
The Central Communication Hub
The Mediator is a behavioral design pattern that reduces chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only through a "mediator" object.
The core idea is to introduce a central "air traffic controller" (the Mediator) for a group of interacting objects (the "Colleagues"). Instead of talking to each other directly, all Colleagues only communicate with the Mediator. When a Colleague's state changes (e.g., a user ticks a checkbox), it notifies the Mediator. The Mediator then contains the logic to orchestrate the necessary actions by calling methods on other Colleagues. This changes the communication structure from a many-to-many mesh to a much simpler one-to-many star configuration.
The Participants
- Mediator: An interface that defines the communication contract. It declares a method that Colleagues use to notify the Mediator of events.
- Concrete Mediator: A class that implements the
Mediator
interface. It knows about and coordinates a group ofConcrete Colleague
objects. It centralizes the complex interaction logic. - Colleague: The components that need to communicate. Each Colleague holds a reference to its
Mediator
and calls it when an event occurs.
Example: A User Registration Dialog
Let's model our registration dialog using the Mediator pattern.
// The Mediator interface
interface IDialogMediator {
void notify(Component sender, String event);
}
// The base Component (Colleague) class
abstract class Component {
protected IDialogMediator mediator;
public Component(IDialogMediator mediator) { this.mediator = mediator; }
}
// Concrete Colleagues
class Button extends Component {
private boolean isEnabled;
public Button(IDialogMediator mediator) { super(mediator); }
public void setEnabled(boolean enabled) { this.isEnabled = enabled; }
public void click() {
// When clicked, the button notifies the mediator.
mediator.notify(this, "click");
}
}
class Checkbox extends Component {
private boolean isChecked;
public Checkbox(IDialogMediator mediator) { super(mediator); }
public void check() {
this.isChecked = !this.isChecked;
// When checked, the checkbox notifies the mediator.
mediator.notify(this, "check");
}
public boolean isChecked() { return this.isChecked; }
}
class TextBox extends Component {
private String text;
public TextBox(IDialogMediator mediator) { super(mediator); }
public void setText(String text) { this.text = text; }
}
// The Concrete Mediator
class RegistrationDialog implements IDialogMediator {
// The mediator knows about all the components.
private Button registerButton;
private Checkbox termsCheckbox;
private TextBox usernameBox;
public void registerComponents(Button b, Checkbox c, TextBox t) {
this.registerButton = b;
this.termsCheckbox = c;
this.usernameBox = t;
}
// It contains the complex interaction logic, centralized in one place.
@Override
public void notify(Component sender, String event) {
if (sender == termsCheckbox && "check".equals(event)) {
System.out.println("Mediator: Checkbox toggled. Enabling/disabling button.");
registerButton.setEnabled(termsCheckbox.isChecked());
}
if (sender == registerButton && "click".equals(event)) {
System.out.println("Mediator: Register button clicked. Processing registration...");
// ... registration logic
}
}
}
// The Client
public class Client {
public static void main(String[] args) {
RegistrationDialog mediator = new RegistrationDialog();
Button button = new Button(mediator);
Checkbox checkbox = new Checkbox(mediator);
TextBox textbox = new TextBox(mediator);
// Register the components with the mediator
mediator.registerComponents(button, checkbox, textbox);
System.out.println("--- Initially, the register button is disabled.");
checkbox.check(); // User checks the box
System.out.println("--- Checkbox is checked, button is now enabled.");
button.click(); // User clicks the button
}
}
Interview Focus: Analysis and Trade-offs
Benefits:
- Reduces Coupling: The primary benefit. It moves the communication web from many-to-many between colleagues to one-to-many between the mediator and colleagues.
- Centralizes Control: The complex interaction logic that was scattered among many components is now centralized in one place, the mediator, which is easier to understand and maintain.
- Enhances Reusability: Individual components (
Button
,Checkbox
) become highly reusable because they no longer have hardcoded dependencies on other specific components.
Drawbacks:
- Mediator as a "God Object": The most significant trade-off is that the Mediator itself can become a monolithic, overly complex class that knows too much. The pattern effectively trades complexity distributed among components for complexity centralized in the mediator.
How Mediator Relates to Other Concepts
- Mediator vs. Observer: This is a key distinction.
- The Observer pattern is typically for unidirectional communication. A Subject broadcasts updates to its Observers, but the Observers don't usually talk back or to each other.
- The Mediator pattern is for multi-directional, collaborative communication. Colleagues notify the Mediator, and the Mediator then orchestrates actions among other colleagues.
- Mediator vs. Facade:
- A Facade provides a simplified, one-way interface to a subsystem. The classes within the subsystem have no knowledge of the Facade.
- A Mediator centralizes two-way communication between a group of peer objects (Colleagues). The Colleagues know about and are coupled to the Mediator.
- Real-World Examples:
- GUI Dialogs: The classic example, as shown above.
- Air Traffic Control: Airplanes (Colleagues) do not communicate directly with each other. They all communicate with the central control tower (Mediator), which coordinates landings and takeoffs.
- Chat Applications (Group Chat): Users (Colleagues) send messages to the chat server (Mediator), which then distributes the message to all other users in the room.