Mediator Pattern(中介者模式)

是什麼?

Mediator Pattern 定義一個中介物件來封裝一組物件之間的互動。各物件不再直接互相引用,而是透過中介者來溝通,降低物件之間的耦合度。

ℹ️GoF 分類

Mediator 屬於行為型模式(Behavioral Pattern),重點在於用一個中心物件來協調多個物件之間的互動。

什麼時候用?

什麼時候不該用?

⚠️過度設計警告

Mediator 很容易變成「上帝物件(God Object)」,把所有邏輯都塞進去。如果物件之間只有簡單的一對一關係,直接呼叫就好。當 Mediator 變得過於龐大時,考慮拆分成多個小 Mediator。

執行流程

1

定義 Mediator 介面

宣告通知方法供 Colleague 呼叫

2

定義 Colleague 基類

持有 Mediator 參考,事件發生時通知 Mediator

3

實作具體 Mediator

接收通知後,協調相關 Colleague 的反應

4

實作具體 Colleague

只跟 Mediator 溝通,不直接引用其他 Colleague

5

Client 組裝

建立 Mediator 和 Colleague,註冊關係

流程解讀:Mediator 把「多對多通訊」改造成「多對一加一對多」。每個 Colleague 只持有 Mediator 的參考,有事件時通知 Mediator。Mediator 知道所有 Colleague,收到通知後根據業務邏輯決定通知哪些 Colleague、以什麼順序通知。這樣 Colleague 之間完全解耦,互動邏輯集中在 Mediator 中管理。

程式碼範例

C# 版本

csharp
// 1. Mediator 介面
public interface IChatRoom
{
    void SendMessage(string message, User sender);
    void AddUser(User user);
}
 
// 2. Colleague 基類
public abstract class User
{
    protected IChatRoom ChatRoom;
    public string Name { get; }
 
    protected User(string name, IChatRoom chatRoom)
    {
        Name = name;
        ChatRoom = chatRoom;
        chatRoom.AddUser(this);
    }
 
    public void Send(string message) => ChatRoom.SendMessage(message, this);
    public abstract void Receive(string message, string fromUser);
}
 
// 3. 具體 Colleague
public class ChatUser : User
{
    public ChatUser(string name, IChatRoom chatRoom) : base(name, chatRoom) { }
 
    public override void Receive(string message, string fromUser)
    {
        Console.WriteLine($"  [{Name}] 收到來自 {fromUser} 的訊息:{message}");
    }
}
 
// 4. 具體 Mediator
public class ChatRoom : IChatRoom
{
    private readonly List<User> _users = new();
 
    public void AddUser(User user)
    {
        _users.Add(user);
        Console.WriteLine($"[聊天室] {user.Name} 加入了聊天室");
    }
 
    public void SendMessage(string message, User sender)
    {
        Console.WriteLine($"[聊天室] {sender.Name} 說:{message}");
        foreach (var user in _users)
        {
            if (user != sender)
                user.Receive(message, sender.Name);
        }
    }
}
 
// 5. 使用
var chatRoom = new ChatRoom();
var alice = new ChatUser("Alice", chatRoom);
var bob = new ChatUser("Bob", chatRoom);
var charlie = new ChatUser("Charlie", chatRoom);
 
alice.Send("大家好!");
// Bob 和 Charlie 收到訊息
 
bob.Send("嗨 Alice!");
// Alice 和 Charlie 收到訊息

TypeScript 版本

typescript
// 1. Mediator 介面
interface ChatRoom {
  sendMessage(message: string, sender: User): void;
  addUser(user: User): void;
}
 
// 2. Colleague
class User {
  constructor(public name: string, private chatRoom: ChatRoom) {
    chatRoom.addUser(this);
  }
 
  send(message: string) { this.chatRoom.sendMessage(message, this); }
 
  receive(message: string, fromUser: string) {
    console.log(`  [${this.name}] 收到來自 ${fromUser} 的訊息:${message}`);
  }
}
 
// 3. 具體 Mediator
class ConcreteChatRoom implements ChatRoom {
  private users: User[] = [];
 
  addUser(user: User) {
    this.users.push(user);
    console.log(`[聊天室] ${user.name} 加入了聊天室`);
  }
 
  sendMessage(message: string, sender: User) {
    console.log(`[聊天室] ${sender.name} 說:${message}`);
    this.users
      .filter(u => u !== sender)
      .forEach(u => u.receive(message, sender.name));
  }
}
 
// 4. 使用
const chatRoom = new ConcreteChatRoom();
const alice = new User("Alice", chatRoom);
const bob = new User("Bob", chatRoom);
const charlie = new User("Charlie", chatRoom);
 
alice.send("大家好!");
bob.send("嗨 Alice!");

Python 版本

python
from abc import ABC, abstractmethod
 
# 1. Mediator 介面
class ChatRoom(ABC):
    @abstractmethod
    def send_message(self, message: str, sender: "User") -> None:
        pass
    @abstractmethod
    def add_user(self, user: "User") -> None:
        pass
 
# 2. Colleague
class User:
    def __init__(self, name: str, chat_room: ChatRoom):
        self.name = name
        self._chat_room = chat_room
        chat_room.add_user(self)
 
    def send(self, message: str) -> None:
        self._chat_room.send_message(message, self)
 
    def receive(self, message: str, from_user: str) -> None:
        print(f"  [{self.name}] 收到來自 {from_user} 的訊息:{message}")
 
# 3. 具體 Mediator
class ConcreteChatRoom(ChatRoom):
    def __init__(self):
        self._users: list[User] = []
 
    def add_user(self, user: User) -> None:
        self._users.append(user)
        print(f"[聊天室] {user.name} 加入了聊天室")
 
    def send_message(self, message: str, sender: User) -> None:
        print(f"[聊天室] {sender.name} 說:{message}")
        for user in self._users:
            if user != sender:
                user.receive(message, sender.name)
 
# 4. 使用
chat_room = ConcreteChatRoom()
alice = User("Alice", chat_room)
bob = User("Bob", chat_room)
charlie = User("Charlie", chat_room)
 
alice.send("大家好!")
bob.send("嗨 Alice!")

Java 版本

java
import java.util.ArrayList;
import java.util.List;
 
// 1. Mediator 介面
public interface ChatRoom {
    void sendMessage(String message, User sender);
    void addUser(User user);
}
 
// 2. Colleague
public class User {
    private final String name;
    private final ChatRoom chatRoom;
 
    public User(String name, ChatRoom chatRoom) {
        this.name = name;
        this.chatRoom = chatRoom;
        chatRoom.addUser(this);
    }
 
    public String getName() { return name; }
 
    public void send(String message) {
        chatRoom.sendMessage(message, this);
    }
 
    public void receive(String message, String fromUser) {
        System.out.println("  [" + name + "] 收到來自 " + fromUser + " 的訊息:" + message);
    }
}
 
// 3. 具體 Mediator
public class ConcreteChatRoom implements ChatRoom {
    private final List<User> users = new ArrayList<>();
 
    public void addUser(User user) {
        users.add(user);
        System.out.println("[聊天室] " + user.getName() + " 加入了聊天室");
    }
 
    public void sendMessage(String message, User sender) {
        System.out.println("[聊天室] " + sender.getName() + " 說:" + message);
        for (User user : users) {
            if (user != sender) {
                user.receive(message, sender.getName());
            }
        }
    }
}
 
// 4. 使用
ChatRoom chatRoom = new ConcreteChatRoom();
User alice = new User("Alice", chatRoom);
User bob = new User("Bob", chatRoom);
User charlie = new User("Charlie", chatRoom);
 
alice.send("大家好!");
bob.send("嗨 Alice!");

結構圖

Client
creates
Mediator (ChatRoom)
Colleague A (Alice)
communicates via
Colleague B (Bob)
communicates via
Colleague C (Charlie)

結構解讀:三個 Colleague 互相不認識,全部只跟 Mediator 溝通。如果沒有 Mediator,3 個物件之間有 3 條直接連線(N 個物件有 N*(N-1)/2 條)。有了 Mediator,每個物件只有 1 條連線到 Mediator,從網狀依賴變成星狀依賴。Mediator 知道所有 Colleague,收到訊息後根據邏輯決定轉發給誰。

實戰補充

💡資深開發者經驗

MediatR 套件:.NET 生態系的 MediatR 是 Mediator Pattern 的熱門實作,搭配 CQRS 把 Command/Query 的分發解耦。IRequest + IRequestHandler 讓 Controller 只需 _mediator.Send(command) 就好。

表單欄位聯動:前端的複雜表單(選了國家就更新城市選項、勾了某選項就停用另一個欄位)用 Mediator 集中管理聯動邏輯,避免欄位之間互相引用形成義大利麵。

防止 God Object:Mediator 最大的風險是變成 God Object。解法:按業務功能拆分成多個小 Mediator,或搭配 Pipeline Behavior(MediatR 的做法)把橫切關注點(Log、Validation)抽出。

Mediator vs Observer:Observer 是廣播(一對多通知),Mediator 是協調(多對多互動的中心化管理)。Observer 的 Subject 不管 Observer 怎麼反應,Mediator 會根據事件來協調各方行為。

理解測驗

🤔 Mediator Pattern 解決的核心問題是什麼?

🤔 Mediator Pattern 最常見的風險是什麼?

🤔 以下哪個場景最適合用 Mediator Pattern?

面試常見問題

Q: Mediator 和 Observer 都涉及物件之間的溝通,怎麼區分?

A: Observer 是「廣播」— Subject 通知所有 Observer,Subject 不關心 Observer 怎麼反應。Mediator 是「協調」— Mediator 收到事件後,根據業務邏輯決定通知誰、怎麼反應。Observer 的 Subject 和 Observer 是一對多的單向關係,Mediator 的 Colleague 之間是多對多的雙向關係(透過 Mediator 中轉)。

Q: MediatR 在 .NET 中的典型使用方式是什麼?

A: MediatR 把 Controller 和 Handler 解耦。Controller 只做一件事:await _mediator.Send(new CreateOrderCommand(...)) 。MediatR 自動找到對應的 IRequestHandler 來處理。搭配 Pipeline Behavior 可以在 Handler 前後加入日誌、驗證、交易管理等橫切關注點(Cross-Cutting Concerns,指跨越多個模組的共通功能如日誌、授權)。

相關模式

| 模式 | 關係 | |------|------| | Observer | Observer 是單向廣播,Mediator 是雙向協調。當 Observer 的通知鏈變得太複雜時,考慮改用 Mediator | | Facade | 都是中心化的協調者,但 Facade 是單向的(簡化子系統存取),Mediator 是雙向的(管理同事間互動) | | Command | MediatR 就是 Mediator + Command 的結合 — Command 封裝請求,Mediator 負責分發 | | Chain of Responsibility | 都能處理請求分發,但 Mediator 是集中式(一個中心),CoR 是分散式(一條鏈) |

重點整理

💡一句話記住

Mediator Pattern = 機場塔台:所有人只跟塔台說話,塔台決定誰該動。 口訣:「網狀變星狀,塔台管全場」

| 概念 | 說明 | |------|------| | Mediator(中介者介面) | 定義物件之間溝通的介面 | | ConcreteMediator | 實作協調邏輯,知道所有 Colleague | | Colleague(同事) | 只跟 Mediator 溝通,不直接引用其他 Colleague | | 核心好處 | 把網狀依賴簡化為星狀,降低耦合度 | | 代價 | Mediator 可能變成 God Object |

你可能也想看

State PatternChain of Responsibility Pattern

按 ← → 鍵切換課程