The Mediator Design Pattern is a behavioral pattern that defines an object (the Mediator) to encapsulate how a set of objects interact.
It promotes loose coupling by preventing objects from referring to each other directly, and lets you vary their interactions independently.
It’s particularly useful in situations where:
When classes depend on each other directly, their interactions often become tangled.
For example, in a GUI form, when a user types into a field, it may enable or disable a button, update a label, and trigger validation — each component talking to the others directly.
But as more components are added, this direct communication becomes hard to manage, creates spaghetti code, and each component ends up handling its own logic plus knowledge of how to coordinate with others.
The Mediator Pattern solves this by introducing a central object that handles communication between components. Each component interacts only with the mediator, which coordinates and manages interactions — reducing coupling and simplifying each component’s logic.
Let’s walk through a real-world example to see how we can apply the Mediator Pattern to build a more modular, loosely coupled system where components communicate cleanly and consistently.
Imagine you're building a login form with the following UI components:
The logic of the form is simple:
Sounds simple, right? Let’s try implementing this with each component talking directly to the others.
At first glance, this seems fine. But as soon as you add a few more components (e.g., remember me checkbox, forgot password link), the logic starts to spiral out of control.
Every component knows about others: the TextField
knows the Button
, the Button
knows the TextField
and the Label
. A change in one component's logic often requires updates in others — creating a fragile system.
You can’t easily reuse these components elsewhere. They’re hard-wired to interact with specific peers, making them context-dependent.
Adding or modifying interactions requires changing the logic of multiple classes. This violates the Open/Closed Principle and makes the system harder to test and evolve.
Each component contains not only its own logic, but also coordinating behavior, making it harder to isolate responsibilities.
We need a way to:
This is exactly what the Mediator Pattern is designed to solve.
The Mediator Pattern promotes loose coupling by centralizing communication between objects. Instead of having components refer to and interact with each other directly, they communicate through a mediator.
This leads to a cleaner separation of concerns and makes components easier to reuse in different contexts.
Declares a method (commonly notify()
, componentChanged()
, or send()
) used by components to send events to the mediator.
Implements the mediator interface and coordinates communication between registered components. It holds references to all components it manages.
An abstract class or interface for UI elements or modules. It holds a reference to the mediator and delegates communication through it.
These are the actual components (e.g., TextField
, Button
, Label
) that perform actions and notify the mediator when changes occur. They never talk directly to each other.
Let’s refactor our tightly coupled login form using the Mediator Pattern. Each UI component will interact only with the mediator, which will handle all coordination and logic.
UIMediator
InterfaceThis interface defines how components will notify the mediator when their state changes. It provides a method like componentChanged()
to centralize communication.
UIComponent
)This abstract base class holds a reference to the mediator and provides a method to notify it when something changes. All UI elements will extend this class.
This ensures a consistent way for all components to communicate with the mediator.
Each component (e.g., TextField
, Button
, Label
) extends UIComponent
and defines its own local behavior. These components no longer interact with each other directly — they only notify the mediator.
FormMediator
)This class knows about all components and defines the rules for how they should behave in response to one another. When a component changes, the mediator coordinates the appropriate actions.
Now, the client wires everything together — components are instantiated, linked to the mediator, and can be used without knowing about each other.
TextField
, Button
, and Label
can be reused in other contexts