Design Pattern Course
Mediator is a Behavioral Design Pattern.
Mediator promotes loose coupling by keeping objects from referring to one another directly. Instead of components communicating with each other peer-to-peer (creating a web of dependencies where everyone knows about everyone else), they communicate through a central mediator object that orchestrates the interaction. This reduces the number of direct dependencies from O(n²) in a fully connected system to O(n) — each participant only knows about the mediator, not about all the other participants.
A classic real-world analogy: air traffic control. Aircraft don't communicate directly with each other — they communicate through the ATC tower (the mediator), which knows about all flights and manages their interactions. Another: a chat server where users send messages to the server, and the server routes them to other participants, rather than each user holding a direct connection to every other user.
package com.praveen.designpatterns.behavioral.mediator; public interface ChatServer { void addUser(Participant user); void sendMessage(Participant user, String message); }
package com.praveen.designpatterns.behavioral.mediator; public class Participant { private final String userName; private ChatServer chatServerMediator; public Participant(String name) { this.userName = name; } public String getUserName() { return userName; } public void joinChatGroup(ChatServer chatGroup) { this.chatServerMediator = chatGroup; chatServerMediator.addUser(this); } public void sendMessage(String message) { System.out.println(userName + ", Sending: \"" + message + "\""); chatServerMediator.sendMessage(this, message); } public void receiveMessage(String message, Participant from) { System.out.println(userName + ", Received: \"" + message + "\", From: " + from.getUserName()); } }
package com.praveen.designpatterns.behavioral.mediator; import java.util.ArrayList; import java.util.List; public class ChatServerMediator implements ChatServer { private final List<Participant> participants = new ArrayList<>(); @Override public void addUser(Participant user) { participants.add(user); } @Override public void sendMessage(Participant sender, String message) { for (Participant p : participants) { if (p != sender) { p.receiveMessage(message, sender); } } } }
package com.praveen.designpatterns.behavioral.mediator; public class MediatorPatternTest { public static void main(String[] args) { ChatServer chatServer = new ChatServerMediator(); Participant praveen = new Participant("Praveen"); Participant prasad = new Participant("Prasad"); Participant praneeth = new Participant("Praneeth"); praveen.joinChatGroup(chatServer); prasad.joinChatGroup(chatServer); praneeth.joinChatGroup(chatServer); praveen.sendMessage("Hi Everyone, I am Praveen"); praneeth.sendMessage("Hi Praveen, How are you"); } }
Output
Praveen, Sending: "Hi Everyone, I am Praveen"
Prasad, Received: "Hi Everyone, I am Praveen", From: Praveen
Praneeth, Received: "Hi Everyone, I am Praveen", From: Praveen
Praneeth, Sending: "Hi Praveen, How are you"
Praveen, Received: "Hi Praveen, How are you", From: Praneeth
Prasad, Received: "Hi Praveen, How are you", From: Praneeth
The JDK examples listed in the original (java.util.Timer, java.util.concurrent.Executor, java.lang.reflect.Method) are commonly repeated but are a weak fit. They're more accurately described as abstractions over scheduling or invocation, not as mediators that coordinate communication between multiple peer objects. A better real-world Mediator in Java: Spring's ApplicationEventPublisher/ApplicationEventMulticaster — publishers fire events without knowing who's listening, and the Spring event infrastructure (the mediator) routes them to the appropriate @EventListener-annotated methods. Message brokers like RabbitMQ and Kafka serve as mediators at the infrastructure level — producers and consumers don't know about each other; they know only about the broker.
When to Use the Mediator Pattern
- When a set of objects communicate in complex, many-to-many ways and the resulting interdependencies make the system hard to understand and maintain.
- When reusing a component is difficult because it holds references to many other components it communicates with directly — the Mediator breaks those direct references.
- When you want to customize or replace the interaction logic between components without changing the components themselves.
Mediator vs Observer
These two patterns are often confused because both reduce direct coupling between objects. The distinction: Observer establishes a one-to-many pub-sub relationship where a subject notifies many dependents of its own state changes. Mediator centralizes many-to-many communication between peers so none of them need to know about one another — the mediator contains the routing/interaction logic. In practice, a Mediator often uses Observer internally to receive event notifications from participants.