零基礎設計模式——行為型模式 - 中介者模式

第四部分:行為型模式 - 中介者模式 (Mediator Pattern)

接下來,我們學習中介者模式。這個模式用一個中介對象來封裝一系列的對象交互。中介者使各個對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。

  • 核心思想:用一個中介對象來封裝一系列的對象交互。中介者使各個對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。

中介者模式 (Mediator Pattern)

“用一個中介對象來封裝一系列的對象交互。中介者使各個對象不需要顯式地相互引用,從而使其耦合松散,而且可以獨立地改變它們之間的交互。” (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.)

想象一個機場的控制塔:

  • 飛機 (Colleague Objects):多架飛機在機場附近飛行,準備降落或起飛。
  • 控制塔 (Mediator):控制塔負責協調所有飛機的行動。飛機不直接相互通信來決定誰先降落、誰使用哪個跑道。它們都只與控制塔通信。
  • 通信 (Interaction):飛機向控制塔報告自己的狀態和意圖(如請求降落),控制塔根據整體情況向飛機發出指令(如允許降落、指定跑道、等待)。

如果沒有控制塔,飛機之間需要復雜的直接通信來避免碰撞和混亂,這將形成一個復雜的網狀通信結構。控制塔將這種網狀結構簡化為星型結構,所有通信都通過中心節點(控制塔)進行。

1. 目的 (Intent)

中介者模式的主要目的:

  1. 減少對象間的直接依賴:將對象間復雜的網狀依賴關系轉變為星型依賴關系。各個同事對象不再直接相互引用,而是都只引用中介者。
  2. 集中控制交互邏輯:對象間的交互邏輯被封裝在中介者對象中,使得交互邏輯更易于理解和維護。
  3. 促進松耦合:同事對象之間松散耦合,它們可以獨立地變化和復用。
  4. 提高系統的可擴展性:當需要修改或增加交互行為時,通常只需要修改中介者類,或者增加新的中介者類,而不需要修改大量的同事類。

2. 生活中的例子 (Real-world Analogy)

  • 聊天室 (Chat Room)

    • 用戶 (Colleague Objects):聊天室中的多個用戶。
    • 聊天室服務器 (Mediator):用戶發送消息給服務器,服務器再將消息廣播給聊天室中的其他用戶(或特定用戶)。用戶之間不直接點對點發送消息。
  • 聯合國安理會 (UN Security Council)

    • 國家 (Colleague Objects):各個成員國。
    • 安理會 (Mediator):國家之間通過安理會這個平臺進行溝通、協調和決策,而不是所有國家都兩兩直接談判所有事務。
  • GUI應用程序中的對話框 (Dialog Box)

    • 各種控件 (Colleague Objects):如按鈕、文本框、列表框、復選框等。
    • 對話框本身 (Mediator):對話框負責協調其內部控件之間的交互。例如,當一個列表框的選擇改變時,對話框可能會啟用或禁用某個按鈕,或者更新某個文本框的內容。控件之間不直接相互操作。

3. 結構 (Structure)

中介者模式通常包含以下角色:

  1. Mediator (中介者接口/抽象類):定義一個接口用于與各個同事對象通信。它通常包含一個方法,供同事對象在自身狀態改變時通知中介者。
  2. ConcreteMediator (具體中介者):實現 Mediator 接口。它了解并維護所有的具體同事類,并負責協調它們之間的交互。它封裝了同事之間的復雜交互邏輯。
  3. Colleague (同事接口/抽象類):定義一個接口,包含一個指向中介者對象的引用。每個同事類都知道其中介者對象。
  4. ConcreteColleague (具體同事類):實現 Colleague 接口。每個具體同事類只知道自己的行為,當需要與其他同事交互時,它會通知中介者,由中介者去協調。
    在這里插入圖片描述
    工作流程
  • 客戶端創建具體中介者對象和所有具體同事對象。
  • 客戶端將中介者對象設置給每個同事對象,并將每個同事對象注冊到中介者中。
  • 當一個同事對象發生變化或需要與其他同事交互時(例如,用戶在GUI中點擊了一個按鈕 ConcreteColleagueA):
    • 該同事對象會調用其 changed() 方法,通知中介者 (ConcreteMediator)。
    • 中介者接收到通知后,根據預定義的交互邏輯,可能會調用其他同事對象的方法(例如,中介者調用 ConcreteColleagueB.someOperationB())。
  • 同事對象之間不直接通信,所有交互都通過中介者進行。

4. 適用場景 (When to Use)

  • 當一組對象以定義良好但復雜的方式進行通信,導致了對象之間存在大量相互依賴和直接引用時(網狀結構)。
  • 當一個對象引用其他很多對象并且直接與這些對象通信,導致難以復用該對象時。
  • 當你想自定義一個分布在多個類中的行為,而又不想生成太多子類時。可以將這些行為提取到中介者中。
  • 在GUI設計中,組件之間的復雜交互(例如,一個組件的狀態變化影響其他多個組件)是中介者模式的經典應用場景。

5. 優缺點 (Pros and Cons)

優點:

  1. 減少了類間的依賴:將對象之間復雜的網狀通信結構簡化為星型結構,降低了同事類之間的耦合度。
  2. 集中控制交互:將對象間的交互行為封裝在中介者對象中,使得交互邏輯更易于理解、維護和修改。
  3. 符合迪米特法則:同事類只需要與中介者通信,不需要了解其他同事類。
  4. 提高了同事類的可復用性:由于同事類之間的耦合降低,它們更容易被復用。
  5. 提高了系統的靈活性和可擴展性:修改交互行為通常只需要修改中介者,而不需要修改各個同事類。

缺點:

  1. 中介者可能變得龐大復雜:如果系統中同事對象過多,或者交互邏輯過于復雜,中介者類可能會變得非常龐大,承擔過多的責任,難以維護(演變成上帝類 God Object)。
  2. 增加了中介者類:引入了額外的中介者類,增加了系統的類數量。

6. 實現方式 (Implementations)

讓我們以一個簡單的聊天室為例。用戶 (User) 是同事,聊天室 (ChatRoom) 是中介者。

同事接口 (ChatUser - Colleague)
// chat_user.go (Colleague interface - can be an abstract struct or interface)
package colleague// Mediator 定義了中介者需要暴露給同事的方法
type Mediator interface {SendMessage(message string, sender User)
}// User 定義了同事需要暴露給中介者的方法,以及同事自身的方法
type User interface {GetName() stringReceiveMessage(message string, senderName string)Send(message string) // 同事通過此方法經由中介者發送消息SetMediator(mediator Mediator)
}
// User.java (Colleague abstract class or interface)
package com.example.colleague;import com.example.mediator.ChatMediator;public abstract class User {protected ChatMediator mediator;protected String name;public User(ChatMediator mediator, String name) {this.mediator = mediator;this.name = name;}public String getName() {return name;}// 同事發送消息,通過中介者public abstract void send(String message);// 同事接收消息public abstract void receive(String message, String senderName);
}
具體同事 (ConcreteUser - ConcreteColleague)
// concrete_user.go
package colleagueimport "fmt"// ConcreteUser 實現了 User 接口
type ConcreteUser struct {name     stringmediator Mediator
}func NewConcreteUser(name string) *ConcreteUser {return &ConcreteUser{name: name}
}func (u *ConcreteUser) SetMediator(mediator Mediator) {u.mediator = mediator
}func (u *ConcreteUser) GetName() string {return u.name
}func (u *ConcreteUser) Send(message string) {fmt.Printf("%s sends: %s\n", u.name, message)if u.mediator != nil {u.mediator.SendMessage(message, u) // 通過中介者發送}
}func (u *ConcreteUser) ReceiveMessage(message string, senderName string) {fmt.Printf("%s receives from %s: %s\n", u.name, senderName, message)
}
// ConcreteUser.java (ConcreteColleague)
package com.example.colleague;import com.example.mediator.ChatMediator;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;public class ConcreteUser extends User {public ConcreteUser(ChatMediator mediator, String name) {super(mediator, name);}@Overridepublic void send(String message) {String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));System.out.println(time + " [" + this.name + "] sends: " + message);mediator.sendMessage(message, this);}@Overridepublic void receive(String message, String senderName) {String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));System.out.println(time + " [" + this.name + "] received from [" + senderName + "]: " + message);}
}
中介者接口 (ChatMediator - Mediator)
// chat_mediator.go (Mediator interface - already defined in colleague/chat_user.go for Go)
// For clarity, we can define it in its own package if preferred.
// package mediator
// type ChatMediator interface {
//    SendMessage(message string, sender colleague.User)
//    AddUser(user colleague.User)
// }
// ChatMediator.java (Mediator interface)
package com.example.mediator;import com.example.colleague.User;public interface ChatMediator {void sendMessage(String message, User sender);void addUser(User user);
}
具體中介者 (ConcreteChatRoom - ConcreteMediator)
// concrete_chat_room.go
package mediator // Assuming this is in a 'mediator' packageimport ("../colleague" // Path to colleague package"fmt"
)// ConcreteChatRoom 實現了 colleague.Mediator 接口
type ConcreteChatRoom struct {users []colleague.User
}func NewConcreteChatRoom() *ConcreteChatRoom {return &ConcreteChatRoom{users: make([]colleague.User, 0),}
}func (cr *ConcreteChatRoom) AddUser(user colleague.User) {fmt.Printf("ChatRoom: %s joined.\n", user.GetName())cr.users = append(cr.users, user)user.SetMediator(cr) // 關鍵:將中介者設置給同事
}// SendMessage 是 colleague.Mediator 接口的實現
func (cr *ConcreteChatRoom) SendMessage(message string, sender colleague.User) {for _, u := range cr.users {// 不把消息發回給發送者自己if u.GetName() != sender.GetName() {u.ReceiveMessage(message, sender.GetName())}}
}
// ConcreteChatMediator.java (ConcreteMediator)
package com.example.mediator;import com.example.colleague.User;
import java.util.ArrayList;
import java.util.List;public class ConcreteChatMediator implements ChatMediator {private List<User> users;public ConcreteChatMediator() {this.users = new ArrayList<>();}@Overridepublic void addUser(User user) {System.out.println("ChatRoom: " + user.getName() + " joined the chat.");this.users.add(user);// In Java, the mediator is typically passed to User's constructor,// so no need for user.setMediator(this) here if done in constructor.}@Overridepublic void sendMessage(String message, User sender) {for (User user : users) {// Don't send the message back to the senderif (user != sender) {user.receive(message, sender.getName());}}}
}
客戶端使用
// main.go (示例用法)
/*
package mainimport ("./colleague""./mediator"
)func main() {chatRoom := mediator.NewConcreteChatRoom()user1 := colleague.NewConcreteUser("Alice")user2 := colleague.NewConcreteUser("Bob")user3 := colleague.NewConcreteUser("Charlie")// 用戶加入聊天室,聊天室會自動設置自己為用戶的中介者chatRoom.AddUser(user1)chatRoom.AddUser(user2)chatRoom.AddUser(user3)fmt.Println("\n--- Chatting Starts ---")user1.Send("Hi everyone!")// Expected:// Alice sends: Hi everyone!// Bob receives from Alice: Hi everyone!// Charlie receives from Alice: Hi everyone!fmt.Println("\n")user2.Send("Hello Alice!")// Expected:// Bob sends: Hello Alice!// Alice receives from Bob: Hello Alice!// Charlie receives from Bob: Hello Alice!
}
*/
// Main.java (示例用法)
/*
package com.example;import com.example.colleague.ConcreteUser;
import com.example.colleague.User;
import com.example.mediator.ChatMediator;
import com.example.mediator.ConcreteChatMediator;public class Main {public static void main(String[] args) {ChatMediator chatRoom = new ConcreteChatMediator();User user1 = new ConcreteUser(chatRoom, "Alice");User user2 = new ConcreteUser(chatRoom, "Bob");User user3 = new ConcreteUser(chatRoom, "Charlie");chatRoom.addUser(user1);chatRoom.addUser(user2);chatRoom.addUser(user3);System.out.println("\n--- Chatting Starts ---");user1.send("Hi everyone!");// Expected output will show timestamps and formatted messagesSystem.out.println(""); // For spacinguser2.send("Hello Alice! How are you?");System.out.println("");user3.send("I'm good, thanks for asking!");}
}
*/

7. 總結

中介者模式通過引入一個中心協調對象(中介者),將系統中原本復雜的對象間網狀交互結構轉變為星型結構。這有效地降低了對象之間的耦合度,使得各個對象(同事)可以獨立變化,同時也使得交互邏輯集中在中介者中,更易于管理和維護。雖然它可能導致中介者自身變得復雜,但在處理多對多對象交互的場景下,中介者模式提供了一種優雅且有效的解決方案,尤其在GUI開發和需要解耦多個協作組件的系統中非常有用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/909321.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/909321.shtml
英文地址,請注明出處:http://en.pswp.cn/news/909321.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Day01_C數據結構

01.數據結構 02.段錯誤出現的四種場景 02.實現順序表的頭插、尾插、頭刪、尾刪(釋放順序表) main.c #include "seq.h" int main(){ seq_p Screate_seqlist(); inputall(S); insert_head(S); dele…

觸覺智能RK3576核心板,工業應用之4K超高清HDMI IN視頻輸入

在工業自動化、醫療影像、軌道交通、電力調度等行業&#xff0c;對高質量視覺信號的實時捕捉和分析需求日益提高。傳統工業相機的低分辨率采集模糊了關鍵細節&#xff0c;延遲的處理過程導致生產環節無法形成閉環控制&#xff0c;讓不同硬件之間的協作障礙重重。 觸覺智能RK35…

清新文藝手繪學習教育培訓競標匯報PPT模版分享

簡約手繪花朵PPT模版&#xff0c;讀書學習教育培訓學習總結設計PPT模版&#xff0c;商業競標企業創業總結匯報演講報告PPT模版&#xff0c;創意動物卡通PPT匯報模版&#xff0c;學術報告PPT模版 清新文藝手繪學習教育培訓競標匯報PPT模版分享

【搜狗輸入法】如何使用自定義標點設置來輸出直角引號

【搜狗輸入法】如何使用自定義標點設置來輸出直角引號 前言&#xff1a; 起因是&#xff0c;我在學習Markdown的語法規范的時候 需要用到直角引號「」 但是鍵盤沒法直接打出來&#xff0c;就想用搜狗輸入法的自定義標點 結果發現這功能完全是個雞肋&#xff0c;沒法用 一…

HarmonyOS5 運動健康app(二):健康跑步(附代碼)

一、數據模型&#xff1a;構建運動記錄的數字骨架 代碼通過RunRecord接口定義了跑步數據的核心結構&#xff1a; interface RunRecord {id: string; // 記錄唯一標識date: Date; // 跑步日期distance: number; // 距離&#xff08;公里&#xff09;duratio…

29-Oracle 23ai Flashback Log Placement(閃回日志靈活配置)

小伙伴們有沒有被各種存儲路徑滿導致的業務崩&#xff0c;半夜起來清理的經歷。一不小心 FRA寫滿了&#xff0c;導致了實例hang住。 OCM考試&#xff0c;時不時就會冒出來這個直接給instance hang&#xff0c;本就卡的環境中腦袋都卡殼、無從下手&#xff0c;一臉懵直接崩。 …

React表單處理:如何獲取輸入框(input)的值?(受控組件)

系列回顧&#xff1a; 在前面的文章中&#xff0c;我們已經掌握了State、Props、事件處理、列表渲染和條件渲染。我們的應用已經能展示動態內容并響應用戶的點擊。現在&#xff0c;我們要 tackling 一個非常常見的需求&#xff1a;如何獲取用戶在表單輸入框&#xff08;<inp…

探索現代 Web 開發:從 HTML5 到 Vue.js 的全棧之旅

在當今快速發展的互聯網時代&#xff0c;Web 開發已經成為構建數字世界的重要基石。無論是企業級應用、社交媒體平臺&#xff0c;還是個人博客和電商平臺&#xff0c;Web 技術都在背后默默支撐著這些系統的運行。隨著前端技術的不斷演進&#xff0c;開發者們已經不再局限于傳統…

ElasticSearch聚合查詢從15秒到1.2秒的深度優化實踐

一、問題背景 在金融風控場景中,我們需要對90天內的交易數據進行多維度聚合分析(按風險等級、地區、金額分段等)。隨著數據量增長到日均3000萬+記錄,原有查詢響應時間逐漸惡化至15秒以上,嚴重影響了業務決策效率。 二、原始架構性能分析 1. 集群拓撲 # 原單節點配置 N…

2025.06.09【讀書筆記】|PromptBio:讓生信分析更簡單的AI平臺

文章目錄 一、PromptBio 是什么&#xff1f;二、主要功能介紹1. 對話式智能體&#xff0c;像聊天一樣做分析2. 自動化工作流&#xff0c;省時省力3. 數據管理一站式搞定4. 機器學習也能一鍵搞定5. “無代碼”到“全代碼”&#xff0c;人人都能用 三、適合哪些人用&#xff1f;四…

實戰解析:如何用克魔(KeyMob)等工具構建iOS應用穩定性與數據可觀測體系

在iOS開發項目逐漸走向復雜化的今天&#xff0c;團隊對“可觀測性”的要求正不斷提升。開發者不僅要知道App是否運行正常&#xff0c;更要明確“為什么異常、在哪里異常、是否可復現”。傳統的調試工具往往側重單一維度&#xff0c;要么是資源監控、要么是日志分析&#xff0c;…

如何輕松實現多源混算報表

報表作為綜合業務&#xff0c;數據來源多種多樣。傳統實現多源混合查詢報表要通過 ETL 將數據同庫&#xff0c;但這種方式數據時效性太差使用場景受限。通過邏輯數倉能獲得較強的數據實時性&#xff0c;但體系又過于沉重&#xff0c;為報表業務搭建邏輯數倉有點得不償失。需要一…

Docker|簡單入門

文章目錄 Docker簡介Docker和虛擬機的聯系和區別基本原理和概念鏡像容器倉庫 Docker安裝配置容器化和Dockerfile實踐環節Docker Compose Docker簡介 Docker是一個用于構建build、運行run、傳送share應用程序的平臺&#xff0c;可以把應用程序打包成一個個的集裝箱&#xff0c;…

阿里云云原生數據庫PolarDB和普通云數據庫的區別?

文章目錄 前言一、云數據庫的演進&#xff1a;從“托管”到“原生”的跨越二、PolarDB的核心創新&#xff1a;重新定義云數據庫的能力邊界1. 存算分離架構&#xff1a;打破資源綁定的“枷鎖”2. 多模引擎與兼容生態&#xff1a;降低應用遷移成本3. 智能化運維&#xff1a;讓數據…

SNN學習(4):真實的生物神經學中神經元和人腦結構學習

目錄 一、基礎知識 1 簡單神經元回路中的信號運作 2 高級功能相關的復雜神經元回路 3 細胞體、樹突和軸突 3.1 神經元細胞 3.2 非神經元細胞 3.3 神經膠質細胞 3.4 神經細胞的信號傳遞 3.4.1 動作電位的特性 3.4.2 興奮和抑制 3.4.3 電傳遞 二、大腦皮層及視覺系統…

第六天 界面操作及美化(6.1 建立菜單及異步調用)

6.1 建立菜單及異步調用 在程序中&#xff0c;菜單&#xff08;Menu&#xff09;是一種常見的用戶界面元素&#xff0c;在程序中起到了組織功能、提高用戶體驗、提供快捷方式和幫助文檔等重要作用。通過合理使用菜單&#xff0c;可以使程序的功能更加清晰、操作更加便捷&#…

論文解析:一文弄懂ResNet(圖像識別分類、目標檢測)

目錄 一、相關資源 二、Motivation 三、技術細節 1.殘差學習過程 2.快捷連接類型 (1)Identity Shortcuts&#xff08;恒等捷徑&#xff09; (2)Projection Shortcuts&#xff08;投影捷徑&#xff09; (3)兩種捷徑對比 3.深層瓶頸結構Deeper Bottleneck Architectures…

動態規劃算法的歡樂密碼(二):路徑問題

專欄&#xff1a;算法的魔法世界 個人主頁&#xff1a;手握風云 一、例題講解 1.1. 不同路徑 題目要求是計算從網格的左上角&#xff08;起點&#xff09;到右下角&#xff08;終點&#xff09;的所有不同路徑的數量。機器人每次只能向下或向右移動一步。如下圖所示&#xff0…

嵌入式相關開源項目、庫、資料------持續更新中

嵌入式相關開源項目、庫、資料------持續更新中 學習初期最難找的就是找學習資料了&#xff0c;本貼精心匯總了一些嵌入式相關資源&#xff0c;包括但不限于編程語言、單片機、開源項目、物聯網、操作系統、Linux、計算機等資源&#xff0c;并且在不斷地更新中&#xff0c;致力…

圖像處理與機器學習項目:特征提取、PCA與分類器評估

圖像處理與機器學習項目:特征提取、PCA與分類器評估 項目概述 本項目將完成一個完整的圖像處理與機器學習流程,包括數據探索、特征提取、主成分分析(PCA)、分類器實現和評估五個關鍵步驟。我們將使用Python的OpenCV、scikit-learn和scikit-image庫來處理圖像數據并實現機器…