# Mediator Pattern

## Problem

In a chat application, if each user holds direct references to every other user, adding a new user requires updating all existing users. With N users, you get N*(N-1) connections — an unmaintainable web of dependencies. Any change to communication logic must be replicated across all user objects.

## Solution

Introduce a **ChatRoom** mediator that sits between users. Users send messages to the room, and the room routes them. Users only know about the mediator, not each other. This reduces N*(N-1) connections to N connections.

**Key participants:**
- **Mediator** — interface declaring the communication method
- **ConcreteMediator** — the ChatRoom that routes messages between users
- **Colleague** — the User class that communicates only through the mediator

## When to Use

- Many objects communicate in complex ways, creating a tangled dependency graph
- You want to centralize communication/coordination logic
- Reusing an individual component is difficult because it depends on too many others
- You want to customize communication behavior without subclassing many components

## When to Avoid

- Only two objects communicate — direct coupling is simpler
- The mediator becomes a "god object" that does everything
- Communication patterns are simple and unlikely to change
- Performance-critical systems where the indirection adds latency

## Pseudocode

```
INTERFACE Mediator:
    method send(message, sender)

CLASS ChatRoom IMPLEMENTS Mediator:
    users = []

    method register(user):
        users.add(user)
        user.room = self

    method send(message, sender):
        FOR user IN users:
            IF user != sender:
                user.receive(message, sender.name)

CLASS User:
    name, room

    method send(message):
        room.send(message, self)

    method receive(message, from):
        PRINT "[{name}] received from {from}: {message}"
```

## Python

```python
from __future__ import annotations
from abc import ABC, abstractmethod


class Mediator(ABC):
    @abstractmethod
    def send(self, message: str, sender: User) -> None:
        pass

    @abstractmethod
    def send_private(self, message: str, sender: User, recipient: str) -> None:
        pass


class User:
    def __init__(self, name: str) -> None:
        self.name = name
        self.room: Mediator | None = None
        self.messages: list[str] = []

    def send(self, message: str) -> None:
        if self.room:
            print(f"  [{self.name}] sends: {message}")
            self.room.send(message, self)

    def send_private(self, message: str, recipient: str) -> None:
        if self.room:
            print(f"  [{self.name}] sends privately to {recipient}: {message}")
            self.room.send_private(message, self, recipient)

    def receive(self, message: str, sender_name: str) -> None:
        msg = f"[{self.name}] received from {sender_name}: {message}"
        self.messages.append(msg)
        print(f"  {msg}")


class ChatRoom(Mediator):
    def __init__(self, name: str) -> None:
        self.name = name
        self.users: dict[str, User] = {}

    def register(self, user: User) -> None:
        self.users[user.name] = user
        user.room = self
        print(f"  {user.name} joined #{self.name}")

    def send(self, message: str, sender: User) -> None:
        for name, user in self.users.items():
            if name != sender.name:
                user.receive(message, sender.name)

    def send_private(self, message: str, sender: User, recipient: str) -> None:
        if recipient in self.users:
            self.users[recipient].receive(f"[private] {message}", sender.name)
        else:
            sender.receive(f"User '{recipient}' not found", "System")


def main() -> None:
    room = ChatRoom("general")

    alice = User("Alice")
    bob = User("Bob")
    charlie = User("Charlie")

    room.register(alice)
    room.register(bob)
    room.register(charlie)

    print("\n--- Broadcast ---")
    alice.send("Hello everyone!")

    print("\n--- Another broadcast ---")
    bob.send("Hey Alice!")

    print("\n--- Private message ---")
    charlie.send_private("Secret message", "Alice")

    print("\n--- Private to non-existent user ---")
    alice.send_private("Hello?", "Dave")


if __name__ == "__main__":
    main()
```

**Output:**
```
  Alice joined #general
  Bob joined #general
  Charlie joined #general

--- Broadcast ---
  [Alice] sends: Hello everyone!
  [Bob] received from Alice: Hello everyone!
  [Charlie] received from Alice: Hello everyone!

--- Another broadcast ---
  [Bob] sends: Hey Alice!
  [Alice] received from Bob: Hey Alice!
  [Charlie] received from Bob: Hey Alice!

--- Private message ---
  [Charlie] sends privately to Alice: Secret message
  [Alice] received from Charlie: [private] Secret message

--- Private to non-existent user ---
  [Alice] sends privately to Dave: Hello?
  [Alice] received from System: User 'Dave' not found
```

## Go

```go
package main

import "fmt"

type Mediator interface {
	Send(message string, sender *User)
	SendPrivate(message string, sender *User, recipient string)
}

type User struct {
	Name     string
	room     Mediator
	Messages []string
}

func NewUser(name string) *User {
	return &User{Name: name}
}

func (u *User) Send(message string) {
	if u.room != nil {
		fmt.Printf("  [%s] sends: %s\n", u.Name, message)
		u.room.Send(message, u)
	}
}

func (u *User) SendPrivate(message, recipient string) {
	if u.room != nil {
		fmt.Printf("  [%s] sends privately to %s: %s\n", u.Name, recipient, message)
		u.room.SendPrivate(message, u, recipient)
	}
}

func (u *User) Receive(message, senderName string) {
	msg := fmt.Sprintf("[%s] received from %s: %s", u.Name, senderName, message)
	u.Messages = append(u.Messages, msg)
	fmt.Printf("  %s\n", msg)
}

type ChatRoom struct {
	Name  string
	users map[string]*User
}

func NewChatRoom(name string) *ChatRoom {
	return &ChatRoom{Name: name, users: make(map[string]*User)}
}

func (r *ChatRoom) Register(user *User) {
	r.users[user.Name] = user
	user.room = r
	fmt.Printf("  %s joined #%s\n", user.Name, r.Name)
}

func (r *ChatRoom) Send(message string, sender *User) {
	for name, user := range r.users {
		if name != sender.Name {
			user.Receive(message, sender.Name)
		}
	}
}

func (r *ChatRoom) SendPrivate(message string, sender *User, recipient string) {
	if user, ok := r.users[recipient]; ok {
		user.Receive("[private] "+message, sender.Name)
	} else {
		sender.Receive(fmt.Sprintf("User '%s' not found", recipient), "System")
	}
}

func main() {
	room := NewChatRoom("general")

	alice := NewUser("Alice")
	bob := NewUser("Bob")
	charlie := NewUser("Charlie")

	room.Register(alice)
	room.Register(bob)
	room.Register(charlie)

	fmt.Println("\n--- Broadcast ---")
	alice.Send("Hello everyone!")

	fmt.Println("\n--- Another broadcast ---")
	bob.Send("Hey Alice!")

	fmt.Println("\n--- Private message ---")
	charlie.SendPrivate("Secret message", "Alice")

	fmt.Println("\n--- Private to non-existent user ---")
	alice.SendPrivate("Hello?", "Dave")
}
```

**Output:**
```
  Alice joined #general
  Bob joined #general
  Charlie joined #general

--- Broadcast ---
  [Alice] sends: Hello everyone!
  [Bob] received from Alice: Hello everyone!
  [Charlie] received from Alice: Hello everyone!

--- Another broadcast ---
  [Bob] sends: Hey Alice!
  [Alice] received from Bob: Hey Alice!
  [Charlie] received from Bob: Hey Alice!

--- Private message ---
  [Charlie] sends privately to Alice: Secret message
  [Alice] received from Charlie: [private] Secret message

--- Private to non-existent user ---
  [Alice] sends privately to Dave: Hello?
  [Alice] received from System: User 'Dave' not found
```

## JavaScript

```javascript
class User {
  constructor(name) {
    this.name = name;
    this.room = null;
    this.messages = [];
  }

  send(message) {
    if (this.room) {
      console.log(`  [${this.name}] sends: ${message}`);
      this.room.send(message, this);
    }
  }

  sendPrivate(message, recipient) {
    if (this.room) {
      console.log(`  [${this.name}] sends privately to ${recipient}: ${message}`);
      this.room.sendPrivate(message, this, recipient);
    }
  }

  receive(message, senderName) {
    const msg = `[${this.name}] received from ${senderName}: ${message}`;
    this.messages.push(msg);
    console.log(`  ${msg}`);
  }
}

class ChatRoom {
  constructor(name) {
    this.name = name;
    this.users = new Map();
  }

  register(user) {
    this.users.set(user.name, user);
    user.room = this;
    console.log(`  ${user.name} joined #${this.name}`);
  }

  send(message, sender) {
    for (const [name, user] of this.users) {
      if (name !== sender.name) {
        user.receive(message, sender.name);
      }
    }
  }

  sendPrivate(message, sender, recipientName) {
    const recipient = this.users.get(recipientName);
    if (recipient) {
      recipient.receive(`[private] ${message}`, sender.name);
    } else {
      sender.receive(`User '${recipientName}' not found`, "System");
    }
  }
}

// --- Demo ---
const room = new ChatRoom("general");

const alice = new User("Alice");
const bob = new User("Bob");
const charlie = new User("Charlie");

room.register(alice);
room.register(bob);
room.register(charlie);

console.log("\n--- Broadcast ---");
alice.send("Hello everyone!");

console.log("\n--- Another broadcast ---");
bob.send("Hey Alice!");

console.log("\n--- Private message ---");
charlie.sendPrivate("Secret message", "Alice");

console.log("\n--- Private to non-existent user ---");
alice.sendPrivate("Hello?", "Dave");
```

**Output:**
```
  Alice joined #general
  Bob joined #general
  Charlie joined #general

--- Broadcast ---
  [Alice] sends: Hello everyone!
  [Bob] received from Alice: Hello everyone!
  [Charlie] received from Alice: Hello everyone!

--- Another broadcast ---
  [Bob] sends: Hey Alice!
  [Alice] received from Bob: Hey Alice!
  [Charlie] received from Bob: Hey Alice!

--- Private message ---
  [Charlie] sends privately to Alice: Secret message
  [Alice] received from Charlie: [private] Secret message

--- Private to non-existent user ---
  [Alice] sends privately to Dave: Hello?
  [Alice] received from System: User 'Dave' not found
```

## Related Patterns

- **Observer** — Mediator often uses Observer internally to listen to component events. The difference: Observer is decentralized; Mediator centralizes communication.
- **Facade** — Both provide a simplified interface. Facade simplifies access to a subsystem; Mediator coordinates interactions between equal peers.
- **Event Bus** — An Event Bus is a lightweight Mediator: components publish events; others subscribe. Mediator often has more explicit routing logic.
- **Command** — Mediator routes Commands between components.
