- Shift Elevate
- Posts
- Mediator Pattern: Simplify Object Communication with Centralized Control
Mediator Pattern: Simplify Object Communication with Centralized Control
The Pain of Complex Object Interactions
Picture this: You're building an air traffic control system where multiple aircraft need to coordinate landings and takeoffs. Your initial approach seems logical: aircraft communicate directly with each other to avoid collisions.
But as the airport grows, you realize the chaos: each aircraft needs to know about every other aircraft, constantly checking positions, coordinating runway usage, and negotiating priorities. Adding a new aircraft means every existing aircraft's logic needs updating. It's a tangled web of dependencies where planes are tightly coupled to each other.
The Mediator pattern solves this complexity by introducing a control tower that centralizes all communication, promoting loose coupling and making the system easier to understand and maintain.
Understanding the Mediator Pattern
The Mediator pattern defines an object that encapsulates how a set of objects interact. Instead of objects referring to each other directly, they communicate through the mediator, which acts as a central hub for all interactions.
Think of it like an air traffic control tower: instead of pilots communicating directly with each other (which would be chaotic), they all communicate through the control tower, which coordinates and manages all interactions safely and efficiently.
This pattern promotes Loose Coupling, Centralized Control, and Simplified Communication while reducing the complexity of object interactions.
Without Mediator: Components communicate directly with each other, creating tight coupling. Each component needs to know about every other component, leading to complex dependencies and difficult maintenance.
With Mediator: Components communicate through a central mediator. The mediator coordinates all interactions, and components only need to know about the mediator, not each other.

Mediator Pattern Components
Core Components
Mediator Interface: Defines the communication interface for coordinating operations.
Concrete Mediator: Implements the mediator interface and coordinates all component interactions.
Aircraft Component: Communicates with the tower for landing, takeoff, and position reporting.
Ground Control Component: Receives notifications from the tower about aircraft activities.
Complete Java Implementation
Let's build an air traffic control system that demonstrates the Mediator pattern's power in preventing direct communication chaos.
Mediator Interface
public interface ControlTower {
void requestLanding(Aircraft aircraft);
void requestTakeoff(Aircraft aircraft);
void reportPosition(Aircraft aircraft, String position);
void registerAircraft(Aircraft aircraft);
void notifyGroundControl(GroundControl groundControl);
}Concrete Mediator
public class AirTrafficControlTower implements ControlTower {
private List<Aircraft> aircrafts = new ArrayList<>();
private GroundControl groundControl;
private boolean runwayAvailable = true;
@Override
public void registerAircraft(Aircraft aircraft) {
aircrafts.add(aircraft);
}
@Override
public void notifyGroundControl(GroundControl groundControl) {
this.groundControl = groundControl;
}
@Override
public void requestLanding(Aircraft aircraft) {
if (runwayAvailable) {
runwayAvailable = false;
System.out.println("Tower: " + aircraft.getName() + " cleared for landing");
// Notify other aircraft to hold
for (Aircraft other : aircrafts) {
if (other != aircraft) {
other.receiveMessage("Hold position, " + aircraft.getName() + " is landing");
}
}
// Notify ground control
if (groundControl != null) {
groundControl.receiveNotification(aircraft.getName() + " is landing");
}
} else {
aircraft.receiveMessage("Runway occupied, please hold");
}
}
@Override
public void requestTakeoff(Aircraft aircraft) {
if (runwayAvailable) {
runwayAvailable = false;
System.out.println("Tower: " + aircraft.getName() + " cleared for takeoff");
} else {
aircraft.receiveMessage("Runway occupied, hold for takeoff");
}
}
@Override
public void reportPosition(Aircraft aircraft, String position) {
System.out.println("Tower: " + aircraft.getName() + " reported at " + position);
runwayAvailable = true;
// Notify ground control about aircraft position
if (groundControl != null) {
groundControl.receiveNotification(aircraft.getName() + " at " + position);
}
}
}Aircraft Component
public class Aircraft {
private String name;
private ControlTower tower;
public Aircraft(String name, ControlTower tower) {
this.name = name;
this.tower = tower;
tower.registerAircraft(this);
}
public void requestLanding() {
System.out.println(name + ": Requesting landing clearance");
tower.requestLanding(this);
}
public void requestTakeoff() {
System.out.println(name + ": Requesting takeoff clearance");
tower.requestTakeoff(this);
}
public void reportPosition(String position) {
System.out.println(name + ": Reporting position - " + position);
tower.reportPosition(this, position);
}
public void receiveMessage(String message) {
System.out.println(name + " received: " + message);
}
public String getName() {
return name;
}
}Ground Control Component
public class GroundControl {
private String name;
public GroundControl(String name) {
this.name = name;
}
public void receiveNotification(String message) {
System.out.println(name + " notified: " + message);
}
}Client
public class AirTrafficControllerDemo {
public static void main(String[] args) {
// Create control tower (mediator)
ControlTower tower = new AirTrafficControlTower();
// Create and set ground control
GroundControl groundControl = new GroundControl("Ground Control");
tower.notifyGroundControl(groundControl);
// Create aircraft
Aircraft flight1 = new Aircraft("Flight 101", tower);
Aircraft flight2 = new Aircraft("Flight 202", tower);
System.out.println("=== Air Traffic Control Demo ===\n");
// Flight 1 requests landing
flight1.requestLanding();
System.out.println();
// Flight 2 tries to land while runway is occupied
flight2.requestLanding();
System.out.println();
// Flight 1 reports landed
flight1.reportPosition("Gate A");
System.out.println();
// Now Flight 2 can land
flight2.requestLanding();
}
}Expected Output:
=== Air Traffic Control Demo ===
Flight 101: Requesting landing clearance
Tower: Flight 101 cleared for landing
Flight 202 received: Hold position, Flight 101 is landing
Ground Control notified: Flight 101 is landing
Flight 202: Requesting landing clearance
Flight 202 received: Runway occupied, please hold
Flight 101: Reporting position - Gate A
Tower: Flight 101 reported at Gate A
Ground Control notified: Flight 101 at Gate A
Flight 202: Requesting landing clearance
Tower: Flight 202 cleared for landing
Flight 101 received: Hold position, Flight 202 is landing
Ground Control notified: Flight 202 is landing🚀 Get the Complete Implementation
The full code with advanced mediator implementations and complex interaction scenarios is available in our Design Patterns Repository.
# Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=MediatorPatternTestReal World Examples
The Mediator pattern is extensively used in real world applications:
1. GUI Frameworks
Modern GUI frameworks use the Mediator pattern extensively for component communication. Instead of UI components directly communicating with each other, they communicate through a central mediator that manages events, state changes, and component interactions.
2. Chat Applications
Chat applications use the Mediator pattern to manage communication between users. The chat server acts as a mediator, receiving messages from one user and distributing them to all other users in the chat room.
3. Workflow Management Systems
Business process management systems use the Mediator pattern to coordinate different workflow steps, ensuring that each step communicates through a central workflow engine rather than directly with other steps.
When to Use the Mediator Pattern
Understanding when to apply the Mediator pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:
✅ Ideal Scenarios:
You have a set of objects that communicate in well defined but complex ways.
You want to avoid tight coupling between objects that interact.
You need to reuse objects in different contexts without changing their communication logic.
You want to centralize control over how objects interact.
❌ Skip It When:
The communication between objects is simple and doesn't benefit from centralization.
You have a small number of objects with minimal interactions.
Next Steps: Apply Mediator Pattern in Your Project
Ready to implement the Mediator pattern in your own projects? Here's a structured approach to get you started:
Identify Complex Interactions: Look for areas where multiple objects need to communicate with each other.
Define Communication Protocol: Create a mediator interface that defines how objects should interact.
Implement Central Mediator: Build a concrete mediator that handles all object interactions.
Refactor Components: Modify existing objects to communicate through the mediator.
Test Interaction Scenarios: Ensure all communication flows work correctly through the mediator.
The Mediator pattern transforms complex, tightly coupled object interactions into clean, maintainable systems. By centralizing communication through a mediator, you build systems that are easier to understand, modify, and extend.
Found this helpful? Share it with a colleague who's struggling with complex object interactions. Got questions? We'd love to hear from you at [email protected]