在複雜系統中,多個物件之間往往存在錯綜複雜的相互引用與通訊關係:A 呼叫 B、B 通知 C、C 又回調 A……這種「網狀耦合」導致系統難以維護、擴展與測試。中介者模式透過引入一個「中介者」物件,將多對多的交互關係轉化為一對多的「星狀結構」:所有同事物件不再直接引用彼此,而是透過中介者轉發訊息。本文以訊息傳遞系統為例,完整展示中介者模式如何封裝用戶間的發送/接收邏輯,實現鬆散耦合。
AbstractMessageMediator,定義 Register() 註冊同事、Send() 轉發訊息介面。MessageMediator,維護同事字典 Dictionary<string, AbstractMessage>,實作註冊與路由邏輯:根據接收者 ID 找到對應同事並呼叫其 Insert()。AbstractMessage,持有中介者參考,提供 Send(to, mm) 供客戶端呼叫(轉發給中介者)、Insert(from, mm) 供中介者回呼(實際接收處理)。SqlMessage、XmlMessage,實作不同儲存方式的 Insert(),分別代表 SQL 與 XML 用戶。
public abstract class AbstractMessageMediator
{
public abstract void Register(AbstractMessage abstractMessage);
public abstract string Send(string from, string to, MessageModel mm);
}
public class MessageMediator : AbstractMessageMediator
{
private Dictionary<string, AbstractMessage> _dictionary = new Dictionary<string, AbstractMessage>();
public override void Register(AbstractMessage abstractMessage)
{
if (!_dictionary.ContainsKey(abstractMessage.UserId))
_dictionary.Add(abstractMessage.UserId, abstractMessage);
abstractMessage.AbstractMessageMediator = this; // 關鍵:反向注入中介者參考
}
public override string Send(string from, string to, MessageModel mm)
{
AbstractMessage abstractMessage = _dictionary[to];
if (abstractMessage != null)
return abstractMessage.Insert(from, mm); // 路由至目標同事
return null;
}
}
關鍵設計:
UserId 為鍵,實現 O(1) 查找接收者。Register() 中將 this 注入同事的 AbstractMessageMediator 屬性,建立雙向關聯(同事→中介者)。Send() 封裝路由邏輯:發送者只需知道接收者 ID,不知曉接收者具體類型與儲存方式。public abstract class AbstractMessage
{
private AbstractMessageMediator _abstractMessageMediator;
private string _userId;
public AbstractMessage(string userId) { this._userId = userId; }
public string UserId { get { return _userId; } }
public AbstractMessageMediator AbstractMessageMediator
{
get { return _abstractMessageMediator; }
set { _abstractMessageMediator = value; }
}
// 客戶端呼叫:發送訊息給指定用戶
public string Send(string to, MessageModel mm)
{
return _abstractMessageMediator.Send(_userId, to, mm);
}
// 中介者回呼:實際接收並處理訊息
public abstract string Insert(string from, MessageModel mm);
}
雙向通訊設計精髓:
Send():同事主動發起,委派給中介者路由。Insert():中介者路由後回呼,同事被動接收處理。public class SqlMessage : AbstractMessage
{
public SqlMessage(string userId) : base(userId) { }
public override string Insert(string from, MessageModel mm)
{
return "Sql方式插入Message(" + from + "發送給" + base.UserId + ")"
+ " - 內容:" + mm.Message
+ " - 時間:" + mm.PublishTime.ToString();
}
}
// XmlMessage 結構相同,僅前綴不同
AbstractMessageMediator mediator = new MessageMediator();
AbstractMessage user1 = new SqlMessage("user1");
AbstractMessage user2 = new SqlMessage("user2");
AbstractMessage user3 = new XmlMessage("user3");
AbstractMessage user4 = new XmlMessage("user4");
mediator.Register(user1);
mediator.Register(user2);
mediator.Register(user3);
mediator.Register(user4);
// user1 → user2 (SQL→SQL)
Response.Write(user1.Send("user2", new MessageModel("你好!", DateTime.Now)));
// user3 → user4 (XML→XML)
Response.Write(user3.Send("user4", new MessageModel("吃了嗎?", DateTime.Now)));
Sql方式插入Message(user1發送給user2) - 內容:你好! - 時間:2007-5-19 23:43:19
Sql方式插入Message(user2發送給user1) - 內容:我不好! - 時間:2007-5-19 23:43:19
Xml方式插入Message(user3發送給user4) - 內容:吃了嗎? - 時間:2007-5-19 23:43:19
Xml方式插入Message(user4發送給user3) - 內容:沒吃,你請我? - 時間:2007-5-19 23:43:19
結果驗證:
JsonMessage)只需實作 AbstractMessage 並註冊,零修改既有程式碼。中介者易膨脹為包含所有業務邏輯的「上帝類別」。最佳實務:
IMediator 只暴露必要方法給同事。中介者模式以「星狀結構」取代「網狀耦合」,將物件間複雜交互封裝於中介者內部,實現同事物件的鬆散耦合與獨立演化。本範例以跨儲存類型(SQL/XML)的用戶訊息傳遞為例,完整展示了註冊機制、雙向通訊介面、路由邏輯與客戶端組裝的全流程。實務中廣泛應用於:GUI 對話框協調(按鈕、輸入框、列表互動)、聊天室伺服器、工作流引擎協調器、MVC 架構中的 Controller、微服務 API Gateway 路由等場景。掌握中介者模式,能讓你在面對「多物件複雜協作」的系統設計挑戰時,構建出清晰可控、易於擴展的架構。