設計模式(十八)行為型:中介者模式詳解
中介者模式(Mediator Pattern)是 GoF 23 種設計模式中的行為型模式之一,其核心價值在于通過引入一個中介者對象來封裝一組對象之間的交互,從而降低對象間的耦合度,使對象不必顯式地相互引用,實現松耦合的協作關系。它將原本“網狀”的多對多通信結構轉化為“星型”結構,所有對象僅與中介者通信,由中介者負責協調和轉發消息。中介者模式廣泛應用于圖形用戶界面(GUI)組件交互、聊天室系統、航空交通管制、多玩家游戲協調、微服務編排、事件總線(Event Bus)等需要集中管理復雜交互邏輯的場景,是構建可維護、可擴展、高內聚低耦合系統的架構利器。
一、詳細介紹
中介者模式解決的是“多個對象之間存在復雜的、動態的、多對多的交互關系,導致系統結構混亂、難以理解、修改和擴展”的問題。在傳統設計中,對象(如窗口中的按鈕、文本框、下拉框)為了響應彼此的狀態變化,會直接持有對方的引用并調用其方法。這導致:
- 緊耦合:對象之間相互依賴,修改一個對象可能影響多個其他對象。
- 高復雜度:交互邏輯分散在各個對象中,難以維護。
- 難以復用:對象無法獨立使用,必須依賴特定的協作環境。
- 擴展困難:新增對象或交互邏輯需要修改大量現有代碼。
中介者模式的核心思想是:“將交互邏輯集中化”,引入一個“中介者”(Mediator)作為所有交互的協調中心。各個協作對象(Colleague)不再直接通信,而是將請求發送給中介者,由中介者根據當前系統狀態決定如何處理或轉發該請求。
該模式包含以下核心角色:
- Mediator(中介者接口):定義同事對象用來與中介者通信的接口,通常包含一個或多個通知方法(如
notify(sender, event)
)。 - ConcreteMediator(具體中介者):實現
Mediator
接口,知道所有具體同事對象,并負責協調它們之間的交互。它維護同事對象的引用,并在收到通知時執行相應的協調邏輯。 - Colleague(同事類):定義同事對象的基類或接口,持有對中介者的引用。同事對象通過中介者與其他同事通信。
- ConcreteColleague(具體同事類):繼承或實現
Colleague
,在自身狀態發生變化時,通過中介者發出通知,而不直接調用其他同事的方法。
中介者模式的關鍵優勢:
- 降低耦合度:同事對象之間無直接依賴,僅依賴中介者。
- 集中控制交互邏輯:交互邏輯集中在中介者中,易于理解、修改和擴展。
- 提高可復用性:同事對象可以獨立于其他同事被復用。
- 支持動態配置:中介者可以在運行時動態改變對象間的協作方式。
- 簡化對象設計:同事對象無需維護與其他對象的引用。
與“觀察者模式”相比,中介者關注多對象間的雙向協調,觀察者關注一對多的單向狀態通知;與“命令模式”相比,命令封裝單個請求,中介者協調多個對象的協作;與“外觀模式”相比,外觀為子系統提供統一接口,中介者管理子系統內部組件的交互。
中介者模式適用于:
- 對象間交互復雜且多變。
- 交互邏輯需要集中管理。
- 希望提高對象的獨立性和可復用性。
二、中介者模式的UML表示
以下是中介者模式的標準 UML 類圖:
圖解說明:
Mediator
定義通知接口。ConcreteMediator
知道所有具體同事,并實現協調邏輯。Colleague
持有中介者引用,通過send()
發送事件。- 同事對象通過中介者間接通信。
三、一個簡單的Java程序實例及其UML圖
以下是一個 GUI 對話框的示例,包含“登錄”按鈕、“用戶名”文本框、“密碼”文本框和“記住我”復選框。它們之間的交互由中介者協調。
Java 程序實例
// 中介者接口
interface Mediator {void notify(Component sender, String event);
}// 抽象同事組件
abstract class Component {protected Mediator mediator;public Component(Mediator mediator) {this.mediator = mediator;}public void send(String event) {mediator.notify(this, event);}public abstract void receive(String event);
}// 具體同事:按鈕
class Button extends Component {private boolean enabled = false;private String name;public Button(Mediator mediator, String name) {super(mediator);this.name = name;}public void setEnabled(boolean enabled) {this.enabled = enabled;System.out.println("🔘 [" + name + "] 按鈕狀態: " + (enabled ? "啟用" : "禁用"));}public boolean isEnabled() {return enabled;}@Overridepublic void receive(String event) {if ("enable".equals(event)) {setEnabled(true);} else if ("disable".equals(event)) {setEnabled(false);}}
}// 具體同事:文本框
class TextBox extends Component {private String text = "";private String name;public TextBox(Mediator mediator, String name) {super(mediator);this.name = name;}public void setText(String text) {this.text = text;System.out.println("📝 [" + name + "] 輸入: \"" + text + "\"");// 文本變化時通知中介者send("textChanged");}public String getText() {return text;}@Overridepublic void receive(String event) {// 文本框通常不接收外部控制事件System.out.println("📝 [" + name + "] 收到事件: " + event + " (忽略)");}
}// 具體同事:復選框
class Checkbox extends Component {private boolean checked = false;private String name;public Checkbox(Mediator mediator, String name) {super(mediator);this.name = name;}public void setChecked(boolean checked) {this.checked = checked;System.out.println("?? [" + name + "] 狀態: " + (checked ? "選中" : "未選中"));// 狀態變化時通知中介者send("checkboxChanged");}public boolean isChecked() {return checked;}@Overridepublic void receive(String event) {// 復選框通常不接收外部控制事件System.out.println("?? [" + name + "] 收到事件: " + event + " (忽略)");}
}// 具體中介者:登錄對話框
class LoginDialogMediator implements Mediator {private Button loginButton;private TextBox usernameBox;private TextBox passwordBox;private Checkbox rememberMeBox;public LoginDialogMediator(Button loginButton, TextBox usernameBox, TextBox passwordBox, Checkbox rememberMeBox) {this.loginButton = loginButton;this.usernameBox = usernameBox;this.passwordBox = passwordBox;this.rememberMeBox = rememberMeBox;}@Overridepublic void notify(Component sender, String event) {System.out.println("🔄 中介者收到事件: [" + sender.getClass().getSimpleName() + "] 發出 '" + event + "'");// 根據發送者和事件類型協調交互if (sender == usernameBox && "textChanged".equals(event)) {checkLoginButton();} else if (sender == passwordBox && "textChanged".equals(event)) {checkLoginButton();} else if (sender == rememberMeBox && "checkboxChanged".equals(event)) {if (rememberMeBox.isChecked()) {System.out.println("💡 用戶選擇記住登錄狀態");} else {System.out.println("💡 用戶取消記住登錄狀態");}}}// 檢查是否啟用登錄按鈕private void checkLoginButton() {boolean usernameFilled = usernameBox.getText().trim().length() > 0;boolean passwordFilled = passwordBox.getText().trim().length() > 0;if (usernameFilled && passwordFilled) {loginButton.send("enable"); // 通過中介者啟用按鈕} else {loginButton.send("disable");}}// 提供獲取組件的方法(可選,用于初始化)public Button getLoginButton() { return loginButton; }public TextBox getUsernameBox() { return usernameBox; }public TextBox getPasswordBox() { return passwordBox; }public Checkbox getRememberMeBox() { return rememberMeBox; }
}// 客戶端使用示例
public class MediatorPatternDemo {public static void main(String[] args) {System.out.println("🔐 登錄對話框 - 中介者模式示例\n");// 創建同事對象Button loginButton = new Button(null, "登錄"); // 初始中介者為 nullTextBox usernameBox = new TextBox(null, "用戶名");TextBox passwordBox = new TextBox(null, "密碼");Checkbox rememberMeBox = new Checkbox(null, "記住我");// 創建中介者并注入同事對象LoginDialogMediator mediator = new LoginDialogMediator(loginButton, usernameBox, passwordBox, rememberMeBox);// 將中介者注入到同事對象中loginButton.mediator = mediator;usernameBox.mediator = mediator;passwordBox.mediator = mediator;rememberMeBox.mediator = mediator;// 初始狀態:登錄按鈕應禁用System.out.println("\n--- 初始化 ---");loginButton.send("disable");// 模擬用戶輸入System.out.println("\n--- 用戶輸入用戶名 ---");usernameBox.setText("alice");System.out.println("\n--- 用戶輸入密碼 ---");passwordBox.setText("secret123");// 此時登錄按鈕應被啟用System.out.println("\n? 用戶名和密碼已填寫,登錄按鈕已啟用");// 模擬取消記住我System.out.println("\n--- 用戶取消記住我 ---");rememberMeBox.setChecked(false);// 模擬清空密碼System.out.println("\n--- 用戶清空密碼 ---");passwordBox.setText("");// 登錄按鈕應被禁用System.out.println("\n? 密碼已清空,登錄按鈕已禁用");}
}
實例對應的UML圖(簡化版)
運行說明:
LoginDialogMediator
是具體中介者,協調四個 GUI 組件。- 當用戶名或密碼框內容變化時,發送
textChanged
事件。 - 中介者收到事件后,檢查兩個框是否都非空,決定啟用或禁用登錄按鈕。
- “記住我”復選框狀態變化時,中介者記錄用戶選擇。
- 所有交互邏輯集中在中介者中,組件之間無直接引用。
四、總結
特性 | 說明 |
---|---|
核心目的 | 集中管理對象間交互,降低耦合 |
實現機制 | 星型通信結構,中介者協調轉發 |
優點 | 降低耦合、集中控制、提高復用性、簡化對象 |
缺點 | 中介者可能變得龐大復雜(“上帝對象”)、增加系統抽象層次 |
適用場景 | GUI 交互、聊天室、游戲協調、工作流引擎、事件總線 |
不適用場景 | 對象間交互簡單、交互邏輯穩定、性能極度敏感 |
中介者模式使用建議:
- 避免中介者過度膨脹,可將其拆分為多個子中介者。
- 可結合“觀察者模式”實現事件通知機制。
- 在 Java 中,
java.util.Timer
/TimerTask
或事件總線(如 EventBus)是中介者思想的體現。 - 微服務中的 API Gateway 或 Service Mesh 控制平面是分布式中介者。
架構師洞見:
中介者模式是“集中式協調”與“解耦通信”的哲學體現。在現代架構中,其思想已演變為事件驅動架構(EDA)、服務網格(Service Mesh)、API 網關 和 消息中間件 的核心。例如,在微服務中,服務網格的 Sidecar 代理作為中介者,管理服務間的通信、熔斷、重試;API 網關作為客戶端請求的中介者,負責路由、認證、限流;Kafka 或 RabbitMQ 作為消息中介者,解耦生產者與消費者。未來趨勢是:中介者將與AI 編排引擎結合,AI 動態決策服務調用鏈;在邊緣計算中,邊緣網關作為本地服務的中介者;在量子網絡中,量子中繼器是量子態傳輸的中介者;在元宇宙中,虛擬世界的狀態同步依賴于強大的中介協調系統。
掌握中介者模式,有助于設計出高內聚、低耦合、易維護的復雜交互系統。作為架構師,應在面對“多對象協作”或“交互邏輯復雜”時,主動考慮引入中介者。中介者不僅是模式,更是系統治理的樞紐——它提醒我們:真正的可擴展性,來自于將“混亂的網狀通信”轉化為“有序的星型控制”,讓復雜性被封裝在單一的協調點,而非散布在系統的每一個角落。