The Mediator pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly and lets you vary their interaction independently. Instead of colleagues talking directly, they communicate through the mediator.

Intent and Motivation

Intent: Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

Motivation: A dialog box has text fields, buttons, and checkboxes that must coordinate: the Submit button is enabled only when all fields are valid; selecting “Other” reveals an extra text field. Direct references between every widget create an N² web of dependencies. A DialogMediator receives events from any widget and notifies others as needed. Widgets know only the mediator — not each other. Adding a new widget means updating the mediator, not every existing widget.

Mediators appear in chat rooms, event buses, Redux stores, and air traffic control systems.

Structure (UML-like)

  ┌──────────────┐  notify    ┌──────────────────┐  notify   ┌──────────────┐
│ Colleague A  │ ─────────► │     Mediator     │ ◄──────── │ Colleague B  │
└──────────────┘            ├──────────────────┤            └──────────────┘
                            │ + notify(sender, │
┌──────────────┐            │   event)         │            ┌──────────────┐
│ Colleague C  │ ─────────► │ + register(col)  │ ◄──────── │ Colleague D  │
└──────────────┘            └──────────────────┘            └──────────────┘
  

Participants:

  • Mediator — defines interface for colleague communication.
  • ConcreteMediator — coordinates colleagues; knows all colleague references.
  • Colleague — each colleague knows its mediator; communicates through it, not directly with peers.

Java Example

  import java.util.*;

// Mediator
interface ChatMediator {
    void sendMessage(String msg, User sender);
    void addUser(User user);
}

// Concrete Mediator
class ChatRoom implements ChatMediator {
    private final List<User> users = new ArrayList<>();

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

    public void sendMessage(String msg, User sender) {
        for (User user : users) {
            if (user != sender) {
                user.receive(msg, sender.getName());
            }
        }
    }
}

// Colleague
class User {
    private final String name;
    private final ChatMediator mediator;

    User(String name, ChatMediator mediator) {
        this.name = name;
        this.mediator = mediator;
        mediator.addUser(this);
    }

    String getName() { return name; }

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

    void receive(String msg, String from) {
        System.out.println(name + " received from " + from + ": " + msg);
    }
}

// Usage
ChatMediator room = new ChatRoom();
User alice = new User("Alice", room);
User bob = new User("Bob", room);
alice.send("Hello everyone!");
  

JavaScript Example

  // Mediator — event bus
const eventBus = {
  subscribers: {},

  subscribe(event, callback) {
    if (!this.subscribers[event]) this.subscribers[event] = [];
    this.subscribers[event].push(callback);
  },

  publish(event, data) {
    (this.subscribers[event] || []).forEach(cb => cb(data));
  }
};

// Colleagues — UI components communicate via event bus
class SearchBox {
  constructor() {
    eventBus.subscribe('search:changed', (query) => {
      console.log(`SearchBox: filtering by "${query}"`);
    });
  }
  onInput(query) {
    eventBus.publish('search:changed', query);
  }
}

class ResultList {
  constructor() {
    eventBus.subscribe('search:changed', (query) => {
      console.log(`ResultList: showing results for "${query}"`);
    });
  }
}

new SearchBox();
new ResultList();
new SearchBox().onInput('design patterns');
  

Real-World Use Cases

Framework / System Usage
Redux / Vuex Store mediates between actions, reducers, and UI components.
Java Dialog / Swing Layout managers and dialog mediators coordinate widget interactions.
Message brokers (RabbitMQ, Kafka) Producers and consumers communicate through the broker mediator.
Air traffic control Controllers mediate between aircraft — planes do not coordinate directly.
MVC controllers Controller mediates between Model and View.
Discord / Slack Chat servers mediate message delivery between users.

Pros and Cons

Pros Cons
Eliminates peer-to-peer dependencies between colleagues Mediator can become a “god object” with too much logic
Centralizes interaction logic — easy to understand flow Mediator complexity grows with each new colleague
Single Responsibility — colleagues focus on their own behavior Changes to interaction rules require modifying the mediator
Open/Closed for new colleagues (register with mediator) Can become a bottleneck if all communication flows through one object
Simplifies object protocols — colleagues have uniform interface to mediator Testing requires mocking the entire mediator for unit tests

When to Use vs When NOT to Use

Use when:

  • A set of objects communicate in well-defined but complex ways.
  • Reusing an object is difficult because it refers to many other objects.
  • Behavior distributed between classes should be customizable without subclassing.
  • You want to reduce coupling in a many-to-many communication scenario.

Do NOT use when:

  • Colleagues are few and communication is simple (direct references are fine).
  • The mediator grows so large it becomes unmaintainable (consider decomposing).
  • Real-time peer-to-peer communication is required without central coordination.
  • You only need one-way simplification of a subsystem (use Facade).

Common Mistakes

  1. God mediator — accumulating business logic; keep colleagues responsible for their own behavior.
  2. Colleagues referencing each other directly — bypasses the mediator and reintroduces coupling.
  3. Confusing Mediator with Observer — Mediator coordinates bidirectional colleague interactions; Observer is one-to-many notification.
  4. Not defining a clear mediator interface — ad-hoc event buses without typed events become hard to debug.
  5. Synchronous mediator blocking — slow colleague operations block all communication; consider async patterns.
  • Observer — colleagues can use Observer within the mediator; Mediator is more structured.
  • Facade — simplifies unidirectional access; Mediator handles bidirectional colleague coordination.
  • Command — colleague actions can be encapsulated as commands sent through the mediator.
  • Chain of Responsibility — decentralized request passing; Mediator centralizes coordination.
  • Event Bus / Pub-Sub — architectural patterns that implement Mediator at system scale.