Behavioral Design Patterns
Imagine you are creating a user registration dialog box with several interacting UI components:
Username textbox.Password field.Terms & Conditions checkbox.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 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.
Mediator interface. It knows about and coordinates a group of Concrete Colleague objects. It centralizes the complex interaction logic.Mediator and calls it when an event occurs.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
}
}
Benefits:
Button, Checkbox) become highly reusable because they no longer have hardcoded dependencies on other specific components.Drawbacks: