軟體設計模式學習(二十一)中介者模式

来源:https://www.cnblogs.com/Yee-Q/archive/2020/05/27/12972120.html
-Advertisement-
Play Games

對於那些存在對象之間複雜交互關係的系統,中介者模式提供了一種簡化複雜交互的解決方案,即通過引入一個中介者,將原本對象之間的兩兩交互轉化為每個對象與中介者之間的交互 模式動機 以微信聊天為例,可以用戶與用戶直接聊天,也可以通過微信群聊天。前者的話,用戶要和別的用戶加為好友,即用戶和用戶之間存在多對多關 ...



對於那些存在對象之間複雜交互關係的系統,中介者模式提供了一種簡化複雜交互的解決方案,即通過引入一個中介者,將原本對象之間的兩兩交互轉化為每個對象與中介者之間的交互


模式動機

以微信聊天為例,可以用戶與用戶直接聊天,也可以通過微信群聊天。前者的話,用戶要和別的用戶加為好友,即用戶和用戶之間存在多對多關係,一個用戶如果要將相同的信息發送給所有其他用戶,必須一個一個發送。而如果使用群聊天,一個用戶可以向多個用戶發送相同信息而無須一一進行發送,只需將信息發送到群中即可,群的作用就是將發送者所發送的信息轉發給每一個接收者用戶。

在用戶與用戶直接聊天的設計方案中,用戶對象之間存在很強關聯性,將導致系統出現如下問題:

  • 系統結構複雜
  • 對象可重用性差
  • 系統擴展混亂

根據單一職責原則,我們應儘量將對象細化,使其只負責單一職責。一個由很多對象構成的模塊,為了減少對象兩兩之間複雜的引用關係,我們需要使用中介者模式,這就是中介者模式的模式動機。


模式定義

用一個中介對象來封裝一系列對象交互,中介者使各對象不需顯式地相互引用,從而使其耦合鬆散,而且可以獨立改變它們之間的交互。中介者模式又稱調停者模式,它是一種對象行為型模式。


模式結構

  1. Mediator(抽象中介者)

    定義一個介面,該介面應用於各同事對象之間的通信

  2. ConcreteMediator(具體中介者)

    抽象中介類的子類,通過協調各個同事對象來實現協作行為,瞭解並維護它對各個同事對象的引用。

  3. Colleague(抽象同事類)

    定義各同事的公有方法

  4. ConcreteColleague(具體同事類)

    每一個同事對象都引用一個中介者對象,每一個同事對象在需要時和其他同事對象通信時,先與中介者通信,通過中介者來間接完成與其他同事的通信


模式分析

通過引入中介者對象,可以將系統的網狀結構變成以中介者為中心的星狀結構。在這個形狀結構中,同事對象不再直接與另一個對象聯繫,它通過中介者對象與另一個對象發生相互作用。

如果對象之間存在多對多的相互關係,可以將對象之間的一些交互行為從各個對象中分離出來,並集中封裝在一個中介者對象中,並由該中介者進行通信和協調,這樣對象間的多對多的複雜關係就可以通過簡單的多對一關係實現,符合迪米特法則,即“只與你直接的朋友們通信,而且只要可能,朋友數目越少越好”。

中介者承擔了以下兩方面的職責:

  • 中轉作用:通過中介者提供的中轉作用,各個同事對象不需要顯式引用其他同事,當需要和其他同事進行通信時,通過中介者即可
  • 協調作用:中介者可以更進一步對同事之間的關係進行封裝,同事一致地和中介者進行交互,而不需要指明中介者具體怎麼做。中介者根據封裝在自身內部的邏輯協調,對同事的請求進行進一步處理,將同事成員之間的關係行為進行分離和封裝

模式實例之虛擬聊天室

某論壇會員通過聊天室進行信息交流,普通會員(CommonMember)可以給其他會員發送文本信息,鑽石會員(DiamondMember)既可以給其他會員發送文本信息,還可以發送圖片信息。聊天室可以對不雅字元進行過濾,可以對發送圖片的大小進行控制。

  1. 抽象中介者類 AbstractChatroom(抽象聊天室類)

    public abstract class AbstractChatroom {
        // 註冊同事對象的方法
        public abstract void register(Member member);
        // 同事之間發送文本信息方法
        public abstract void sendText(String from, String to, String message);
        // 同事之間發送圖片信息
        public abstract void sendImage(String from, String to, String image);
    }
    
  2. 抽象同事類 Member(抽象會員類)

    public abstract class Member {
    	// 維持一個抽象中介者的引用,用於調用中介者的方法
        protected AbstractChatroom chatroom;
        protected String name;
    
        public Member(String name) {
            this.name = name;
        }
    
        public AbstractChatroom getChatroom() {
            return chatroom;
        }
    
        public void setChatroom(AbstractChatroom chatroom) {
            this.chatroom = chatroom;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    	
        // 由於不同類型會員發送文本信息和圖片信息的方式不同
        // 因此對這兩個方法進行抽象聲明
        public abstract void sendText(String to, String message);
    
        public abstract void sendImage(String to, String image);
    
        public void receiveText(String from, String message) {
            System.out.println(from + "發送文本給" + this.name + ",內容為:" + message);
        }
    
        public void receiveImage(String from, String image) {
            System.out.println(from + "發送圖片給" + this.name + ",內容為:" + image);
        }
    }
    
  3. 具體中介者類 ChatGroup(具體聊天室類)

    import java.util.Hashtable;
    
    public class ChatGroup extends AbstractChatroom {
    	
        // 定義一個集合對象存儲需要發生交互的同事對象
        private Hashtable<String, Member> members = new Hashtable<String, Member>();
    	
        // 提供註冊方式,將同事對象加入集合中
        @Override
        public void register(Member member) {
            if (!members.contains(member)) {
                members.put(member.getName(), member);
                member.setChatroom(this);
            }
        }
    
        @Override
        public void sendText(String from, String to, String message) {
            Member member = members.get(to);
            String newMessage = message;
            //  模擬不雅字元過濾
            newMessage = message.replace("日", "*");
            // 調用會員接收方法
            member.receiveText(from, newMessage);
        }
    
        @Override
        public void sendImage(String from, String to, String image) {
            Member member = members.get(to);
            //  模擬圖片大小判斷
            if (image.length() > 5) {
                System.out.println("圖片太大,發送失敗");
            } else {
                // 調用會員接收方法
                member.receiveImage(from, image);
            }
        }
    }
    
  4. 具體同事類 CommonMember(普通會員類)

    public class CommonMember extends Member {
    
        public CommonMember(String name) {
            super(name);
        }
    
        @Override
        public void sendText(String to, String message) {
            System.out.println("普通會員發送信息:");
            chatroom.sendText(name, to, message);
        }
    
        @Override
        public void sendImage(String to, String image) {
            System.out.println("普通會員不能發送圖片!");
        }
    }
    
  5. 具體同事類 DiamondMember(鑽石會員類)

    public class DiamondMember extends Member {
    
        public DiamondMember(String name) {
            super(name);
        }
    
        @Override
        public void sendText(String to, String message) {
            System.out.println("鑽石會員發送信息:");
            chatroom.sendText(name, to, message);
        }
    
        @Override
        public void sendImage(String to, String image) {
            System.out.println("鑽石會員發送圖片");
            chatroom.sendImage(name, to, image);
        }
    }
    
  6. 客戶端測試類 Client

    public class Client {
    
        public static void main(String[] args) {
    
            AbstractChatroom happyChat = new ChatGroup();
    
            Member member1, member2, member3, member4, member5;
            member1 = new DiamondMember("張三");
            member2 = new DiamondMember("李四");
            member3 = new CommonMember("王五");
            member4 = new CommonMember("小芳");
            member5 = new CommonMember("小紅");
    
            happyChat.register(member1);
            happyChat.register(member2);
            happyChat.register(member3);
            happyChat.register(member4);
            happyChat.register(member5);
    
            member1.sendText("李四", "李四,你好");
            member2.sendText("張三", "張三,你好");
            member1.sendText("李四", "今天天氣不錯,有日");
            member2.sendImage("張三", "一個很大很大的太陽");
            member2.sendImage("張三", "太陽");
            member3.sendText("小芳", "還有問題嗎?");
            member3.sendText("小紅", "還有問題嗎?");
            member4.sendText("王五", "沒有了,謝謝");
            member5.sendText("王五", "我也沒有了");
            member5.sendImage("王五", "謝謝");
        }
    }
    
  7. 運行結果

如果需要增加新的具體中介類,只需繼承抽象中介者類並實現其中方法即可,新的具體中介者類可以對信息進行不同的處理,客戶端只需修改少量代碼(如果使用配置文件的話可以不修改代碼)

如果增加新的同事類,繼承抽象同事類並實現即可,同事類之間無直接引用關係。本實例中,中介者對同事類的引用建立在抽象層,因此在客戶端實例化新增的同事類即可直接使用該對象


模式優缺點

中介者模式優點:

  1. 簡化對象間的交互。用中介者和同事的一對多交互代替了原來同事之間多對多交互,更利於理解、維護和擴展。
  2. 將各同事解耦。我們可以獨立的改變、增加和復用各同事和中介者,符合開閉原則
  3. 減少子類生成。中介者將原本分佈於多個對象的行為集合在一起,改變這些行為只需生成新的中介者子類即可,各個同事類可被重用。

中介者模式缺點:

  1. 具體中介者類包含了同事之間的交互細節,可能會導致中介者類非常複雜,難以維護。

模式適用環境

以下情況可以使用中介者模式:

  • 系統對象之間存在複雜的引用關係
  • 一個對象由於引用了其他很多對象並直接與其通信,導致難以復用該對象
  • 想通過一個中間類來封裝多個類的行為,而又不想生成太多子類,可以使用中介者類,在中介者類中定義對象交互的公共行為


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.更新變數 一個變數被重新賦值後,它原有的值會被覆蓋,變數值將會以最後一次賦值為準。 2.同時聲明多個變數 var age = 18 , address ='火影村' , gz = 2000 ; 3.聲明變數的特殊情況: // 2.聲明多個變數 var age =18, address = '火影 ...
  • 在我開發的很多系統裡面,包括Winform混合框架、Bootstrap開發框架等系列產品中,我都傾向於動態配置菜單,並管理對應角色的菜單許可權和頁面許可權,實現系統對用戶許可權的控制,菜單一般包括有名稱、圖標、順序、URL連接等相關信息,對於VUE+Element 前端應用來說,應該原理上差不多,本篇隨筆... ...
  • 關於瀏覽器相容問題 目前主流瀏覽器有IE,edge,Chrome,firefox,Opera(歐朋),Safari(指南針) 由於目前的電腦操作系統很多,瀏覽器的版本迭代有很強,因此用戶的使用瀏覽器是參差不齊的,因此我們作為前端人員在設計網站在不同瀏覽器顯示效果時,相容性就很有必要考慮,最追求給用戶 ...
  • 第一步: 首先進入自己的博客園 -- 點擊<<設置>>--找到 <<博客側邊欄公告(支持HTML代碼) (支持 JS 代碼)>> 第二步: 加入如下代碼 <img style="width: 200px; height: 200px;" src="頭像路徑" alt="qq377905687的頭像" ...
  • 方法 說明 歸屬 alert(msg) 瀏覽器彈出警示框 瀏覽器 console.log(msg) 瀏覽器控制台列印輸出信息 瀏覽器 prompt(info) 瀏覽器彈出輸入框,用戶可以輸入 瀏覽器 程式示例: <!DOCTYPE html> <html lang="en"> <head> <met ...
  • 1.行內式js(很少使用) 以on開頭,如onclick HTML中推薦雙引號,JS推薦單引號 2.內嵌式js(常用) <script> alert('hello world'); <script> 3.外部js文件 <script src="my.js"></script> 利於代碼結構化 便於文 ...
  • 突發奇想,感覺ui組件的上傳圖片用著很方便,突然件想自己寫一個探索其原理 頭像上傳功能主要是前端通過formData將文件傳至伺服器然後伺服器將圖片保存至文件夾,並將其路徑返回至前端,前端渲染至頁面。 前端頁面如下所示(主要是為了寫一個簡單的例子): js部分:這裡使用的是原生的ajax請求將文件信 ...
  • 代碼示例:新聞頁面: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...