Define an object that encapsulates how a set of objects interact — promote loose coupling by preventing direct references between objects.
Mediator centralises complex communications and control logic between related objects (colleagues). Instead of objects referencing each other directly, they all communicate through the mediator. This reduces the number of connections from O(N²) to O(N).
Without Mediator: N objects communicating directly = N×(N-1)/2 connections. With Mediator: each object has exactly one connection — to the mediator.
You have a dialog form with many UI components — a TextBox, a CheckBox, a SubmitButton, a ListBox. When the TextBox changes, the SubmitButton should enable/disable. When the CheckBox is ticked, the ListBox should hide. Every component knows about every other — a tightly coupled web.
Mediator (the Dialog itself) receives notifications from all components and decides what to do. Each component only knows the mediator — zero knowledge of siblings.
notify(sender, event).// Mediator interface public interface Mediator { void notify(Component sender, String event); } // Base Colleague — knows only about the mediator public abstract class Component { protected Mediator mediator; public Component(Mediator m) { this.mediator = m; } public void changed(String event) { mediator.notify(this, event); // all colleagues talk only to mediator } } // Concrete Colleagues public class TextBox extends Component { private String text = ""; public TextBox(Mediator m) { super(m); } public void setText(String t) { text = t; changed("textChanged"); } public String getText() { return text; } } public class CheckBox extends Component { private boolean checked = false; public CheckBox(Mediator m) { super(m); } public void toggle() { checked = !checked; changed("toggled"); } public boolean isChecked() { return checked; } } public class Button extends Component { private boolean enabled = false; public Button(Mediator m) { super(m); } public void setEnabled(boolean e) { enabled = e; System.out.println("Button " + (e ? "enabled" : "disabled")); } public boolean isEnabled() { return enabled; } } // Concrete Mediator — all coordination logic here public class DialogMediator implements Mediator { public TextBox loginInput; public CheckBox rememberMe; public Button submitBtn; public void notify(Component sender, String event) { if (sender == loginInput && event.equals("textChanged")) { // Enable submit only when text is not empty submitBtn.setEnabled(!loginInput.getText().isBlank()); } if (sender == rememberMe && event.equals("toggled")) { // Log the preference System.out.println("Remember me: " + rememberMe.isChecked()); } } } // Client — wires everything public class Main { public static void main(String[] args) { DialogMediator dialog = new DialogMediator(); dialog.loginInput = new TextBox(dialog); dialog.rememberMe = new CheckBox(dialog); dialog.submitBtn = new Button(dialog); dialog.loginInput.setText("user@example.com"); // Button enabled dialog.loginInput.setText(""); // Button disabled dialog.rememberMe.toggle(); // Remember me: true } }
ApplicationEventPublisher — event mediator between publishers and listenersJDialog — dialog coordinates its child components acting as mediatornotify() — it becomes a massive if-else chain handling every possible event from every colleague, defeating the purpose of separation// ❌ BREAKING — infinite loop: A notifies → mediator updates B → B fires → loop public void notify(Component sender, String event) { if (sender == checkBox) { textBox.setText("auto-filled"); // setText fires "textChanged" // → mediator.notify(textBox, "textChanged") // → which might update checkBox → infinite loop! } } // ✅ FIX — re-entrancy guard private boolean handling = false; public void notify(Component sender, String event) { if (handling) return; // ✅ prevent re-entrant notification handling = true; try { if (sender == checkBox) { textBox.setText("auto-filled"); // won't re-enter } } finally { handling = false; } } // ❌ BREAKING — colleague bypasses mediator public class CheckBox extends Component { private Button submitBtn; // ❌ direct reference to another colleague! public void toggle() { submitBtn.setEnabled(checked); // ❌ direct call — bypasses mediator } } // ✅ FIX — only notify mediator, never reference other colleagues public class CheckBox extends Component { public void toggle() { checked = !checked; changed("toggled"); // ✅ only calls mediator.notify(this, "toggled") } }
notify(sender, event). ConcreteMediator holds references to all colleagues. Each colleague holds a mediator reference and calls mediator.notify(this, event) on change.