Mediator Pattern

The Mediator Pattern is a behavioral design pattern that reduces tight coupling between objects by introducing a dedicated mediator. Instead of letting each object reference every other object, they communicate indirectly through the mediator. This centralization of interaction logic simplifies the system’s structure and makes it easier to maintain and extend.

Core Idea

At its heart, the pattern trades spatial complexity for temporal simplicity. By moving the interaction logic out of the individual objects and into a single mediator, each object becomes simpler and only needs to know about the mediator. When the interaction rules change, the mediator is the only place that requires modification, leaving the collaborating objects untouched.

Structural Roles

Role Responsibility
Mediator Declares an interface for communication between colleagues. Typically contains a method that processes notifications from colleagues.
Concrete Mediator Implements the mediator interface, keeps references to the colleague objects, and orchestrates their interactions.
Colleague Holds a reference to the mediator and uses it to send and receive messages. A colleague knows only its own state and the mediator, not the other colleagues.

These roles form a loose coupling network where the mediator is the single point of contact.

Practical Example: A Chat Room

Below is a concise Java illustration of a chat room scenario. Without a mediator, each user would need direct references to every other user to send messages. With the mediator, users only interact with the chat room, which handles broadcasting.

// 1. Mediator interface
interface ChatRoom {
    void sendMessage(String message, User sender);
}

// 2. Concrete mediator
class ConcreteChatRoom implements ChatRoom {
    private final List<User> users = new ArrayList<>();

    void addUser(User user) {
        users.add(user);
    }

    @Override
    public void sendMessage(String message, User sender) {
        for (User u : users) {
            if (u != sender) {
                u.receive(message);
            }
        }
    }
}

// 3. Colleague
class User {
    private final String name;
    private final ChatRoom chatRoom;

    User(String name, ChatRoom chatRoom) {
        this.name = name;
        this.chatRoom = chatRoom;
    }

    void send(String message) {
        System.out.println(name + " sends: " + message);
        chatRoom.sendMessage(message, this);
    }

    void receive(String message) {
        System.out.println(name + " receives: " + message);
    }
}

// 4. Client
public class Main {
    public static void main(String[] args) {
        ChatRoom room = new ConcreteChatRoom();
        User alice = new User("Alice", room);
        User bob   = new User("Bob",   room);

        room.addUser(alice);
        room.addUser(bob);

        alice.send("Hello, Bob!");
    }
}

The mediator (ConcreteChatRoom) handles all message routing, while each User remains unaware of the other participants.

Advantages

  • Reduced Coupling – Colleagues depend only on the mediator, not on each other, aligning with the Dependency Inversion Principle.
  • Centralized Control – Interaction logic lives in one place, making it easier to audit, modify, or extend.
  • Scalability – Adding new colleagues is straightforward: register them with the mediator without touching existing code.

Potential Drawbacks

  • Mediator Complexity – If the interaction logic grows, the mediator can become a large, monolithic component, potentially becoming a new maintenance hotspot.
  • Performance Overhead – All communication passes through the mediator, which may introduce latency in high‑frequency or real‑time systems.

When to Use It

The Mediator Pattern shines in scenarios such as:

  • Complex Interaction Networks – Many objects need to coordinate frequently, and the relationships are intricate.
  • Event‑Driven Systems – Centralized event dispatching (e.g., GUI frameworks, message buses).
  • Microservices Communication – Abstracting inter‑service messaging layers to decouple services.
  • State Machines – Managing transitions between states where multiple components must react to changes.

Typical real‑world applications include GUI event handling, chat servers, collaborative editors, and orchestrating microservice workflows.

Summary

By introducing a mediator, the pattern decouples colleagues, centralizes interaction logic, and enhances maintainability. While it can lead to a more complex mediator in some cases, the trade‑off often results in cleaner, more modular codebases, especially in systems where many objects must coordinate.