Java設計模式之十 ---- 訪問者模式和中介者模式

来源:https://www.cnblogs.com/zhaosq/archive/2019/02/13/10219660.html
-Advertisement-
Play Games

前言 2018年已經過去,新的一年工作已經開始,繼續總結和學習Java設計模式。 在上一篇中我們學習了行為型模式的解釋器模式(Interpreter Pattern)和迭代器模式(Iterator Pattern)。本篇則來學習下行為型模式的兩個模式,訪問者模式(Visitor Pattern)和中 ...


前言

2018年已經過去,新的一年工作已經開始,繼續總結和學習Java設計模式。

上一篇中我們學習了行為型模式的解釋器模式(Interpreter Pattern)和迭代器模式(Iterator Pattern)。本篇則來學習下行為型模式的兩個模式,訪問者模式(Visitor Pattern)和中介者模式(Mediator Pattern)。

訪問者模式

簡介

訪問者模式(VisitorPattern),顧名思義使用了這個模式後就可以在不修改已有程式結構的前提下,通過添加額外的訪問者來完成對已有代碼功能的提升,它屬於行為模式。訪問者模式的目的是封裝一些施加於某種數據結構元素之上的操作。一旦這些操作需要修改的話,接受這個操作的數據結構則可以保持不變。
其主要目的是將數據結構與數據操作分離。

訪問者模式可以說是設計模式中最難以理解的一個模式,因為相比其它模式而言,它過於”繞“了。但是我們可以通過生活中的一些例子來理解它,比如家裡來了客人,客人就是訪問者,他可以做一些事情,但是又不能做全部的事情; 又或者說去網吧上網的小明,小明也是訪問者,他可以在網吧玩游戲、看視頻、聽音樂等等,但是不能破壞網吧中的設備等等。按照這麼理解,我們大概就可以知道訪問者模式主要是做什麼了。

訪問者模式主要由這五個角色組成,抽象訪問者(Visitor)、具體訪問者(ConcreteVisitor)、抽象節點(Node)、具體節點(ConcreteNode)和結構對象(ObjectStructure)。

  • 抽象訪問者(Visitor)角色:聲明瞭一個或者多個方法操作,形成所有的具體訪問者角色必須實現的介面。
  • 具體訪問者(ConcreteVisitor)角色:實現抽象訪問者所聲明的介面,也就是抽象訪問者所聲明的各個訪問操作。
  • 抽象節點(Node)角色:聲明一個接受操作,接受一個訪問者對象作為一個參數。
  • 具體節點(ConcreteNode)角色:實現了抽象節點所規定的接受操作。
  • 結構對象(ObjectStructure)角色:有如下的責任,可以遍歷結構中的所有元素。

示例圖如下:
在這裡插入圖片描述

這裡為了方便理解,我們使用一個簡單的示例來加以說明。
圖書館有一臺電腦,有兩個賬戶,其中一個是管理員的賬戶,擁有所有許可權,但是設置了密碼;另一個賬戶是不需要密碼,但是只能玩游戲和看圖片。張三和李四先後使用了這臺電腦,那麼他們就可以當作是訪問者。
那麼我們便可以根據這裡例子來使用訪問者模式進行開發,首先定義一個抽象的訪問者,擁有玩游戲和看圖片的方法;然後再定義一個抽象節點電腦,接受這個請求。
那麼這個抽象類的代碼如下:


interface Visitor {
   void visit(Games games);
   void visit(Photos photos);
}

interface Computer {
   void accept(Visitor visitor);
}

定義好該抽象類之後,我們需要設計不同的訪問者對節點進行不同的處理。並且需要設計具體節點類實現剛剛抽象節點的方法。

那麼代碼如下:

class ZhangSan implements Visitor {
   @Override
   public void visit(Games games) {
       games.play();
   }

   @Override
   public void visit(Photos photos) {
       photos.watch();
   }
}

class LiSi implements Visitor {
   @Override
   public void visit(Games games) {
       games.play();
   }
   @Override
   public void visit(Photos photos) {
       photos.watch();
   }
}

class Games implements Computer {
   @Override
   public void accept(Visitor visitor) {
       visitor.visit(this);
   }

   public void play() {
       System.out.println("play lol");
   }
}

class Photos implements Computer {
   @Override
   public void accept(Visitor visitor) {
       visitor.visit(this);
   }
   
   public void watch() {
       System.out.println("watch scenery photo");
   }
}

最後我們還需要定義一個結構對象角色,提供一個的介面並允許該訪問者進行訪問,它可以對這些角色進行增加、修改或刪除等操作和遍歷。
代碼如下:

class ObjectStructure {

    private List<Computer> computers = new ArrayList<Computer>();

    public void action(Visitor visitor) {
        computers.forEach(c -> {
            c.accept(visitor);
        });
    }
    public void add(Computer computer) {
        computers.add(computer);
    }
}

編寫好之後,那麼我們來進行測試。
測試代碼如下:


public static void main(String[] args) {
       // 創建一個結構對象
       ObjectStructure os = new ObjectStructure();
       // 給結構增加一個節點
       os.add(new Games());
       // 給結構增加一個節點
       os.add(new Photos());
       // 創建一個訪問者
       Visitor visitor = new ZhangSan();
       os.action(visitor);

}

輸出結果:

play lol
watch scenery photo

訪問者模式優點:

擴展性好,可以在不修改對象結構中的元素的情況下,為對象結構中的元素添加新的功能;
符合單一職責原則,通過訪問者將無關的行為分離,使職責單一;

訪問者模式缺點:

違反了迪米特原則,因為具體元素對訪問者公佈細節;
違反了依賴倒置原則,依賴了具體類,沒有依賴抽象;
對象結構變化困難,若對象結構發生了改變,訪問者的介面和訪問者的實現也都要發生相應的改變;

使用場景:

對象結構中對象對應的類很少改變,但經常需要在此對象結構上定義新的操作;
需要對一個對象結構中的對象進行很多不同的並且不相關的操作,而需要避免讓這些操作"污染"這些對象的類,也不希望在增加新操作時修改這些類。

中介者模式

簡介

中介者模式(Mediator Pattern),定義了一個中介對象來封裝一系列對象之間的交互關係。中介者使各個對象之間不需要顯式地相互引用,從而使耦合性降低,而且可以獨立地改變它們之間的交互行為,屬於行為型模式。
其主要的目的是用來降低多個對象和類之間的通信複雜性。

簡單的來說就是提供一個平臺。比如生活中我們經常用到的聊天軟體QQ、微信群,或者是上網購物的網站淘寶、京東,又或者是房產中介。但是無論是QQ群,還是房產中介,他們都是充當一個中間平臺的作用,我們可以直接通過這個平臺得到我們想要的信息,避免了獨自獲取花費的成本。

中介者模式主要由這四個角色組成, 抽象中介者(Mediator)、具體中介者(ConcreteMediator)、 抽象同事類(Colleague)和具體同事類(ConcreteColleague) 。

  • 抽象中介者(Mediator): 定義了同事對象到中介者對象之間的介面。
  • 具體中介者(ConcreteMediator): 實現抽象中介者的方法,它需要知道所有的具體同事類,同時需要從具體的同事類那裡接收信息,並且向具體的同事類發送信息。
  • 抽象同事類(Colleague): 定義了中介者對象的介面,它只知道中介者而不知道其他的同事對象。
  • 具體同事類(ConcreteColleague) : 每個具體同事類都只需要知道自己的行為即可,但是他們都需要認識中介者。

示例圖如下:
在這裡插入圖片描述

這裡為了方便理解,我們使用一個簡單的示例來加以說明。
xuwujing創建了一個Java的QQ群,並邀請了很多人進來,其中張三也加進來了,進群之後,大家開始互相打招呼進行交流。。。
那麼我們便可以根據這個簡單的例子來使用中介者模式進行開發。
首先依舊定義一個抽象的中介者,就是QQ群,可以進行交流;然後再定義一個抽象的同事類,可以談話。
那麼這個抽象類的代碼如下:


interface QQqun {
   void exchange(Person person,String message);
}

abstract class Person{
   protected String name;
   protected QQqun qun;  
   Person(String name,QQqun qun){
       this.name = name;
       this.qun = qun;
   }
}

定義好該抽象類之後,我們再來定義具體的同事類,也就是xuwujing和張三,可以進行交流。

那麼代碼如下:

class ZhangSan extends Person{

   ZhangSan(String name, QQqun qun) {
       super(name, qun);
   }
   
    void exchange(String message){
       qun.exchange(this,message);
   }
   
    void talk(String message){
       System.out.println(name +"說:" + message);
   }
}

class XuWuJing extends Person{

   XuWuJing(String name, QQqun qun) {
       super(name, qun);
   }
   
    void exchange(String message){
       qun.exchange(this,message);
   }
   
    void talk(String message){
       System.out.println(name +"回應:" + message);
   }
}

最後再來定義具體中介者對象,這個QQ群的具體實現。
代碼如下:

class JavaQQqun implements QQqun{
    private ZhangSan zs;
    private XuWuJing xwj;

    public ZhangSan getZs() {
        return zs;
    }

    public void setZs(ZhangSan zs) {
        this.zs = zs;
    }

    public XuWuJing getXwj() {
        return xwj;
    }


    public void setXwj(XuWuJing xwj) {
        this.xwj = xwj;
    }


    @Override
    public void exchange(Person person, String message) {
            if(zs.equals(person)){
                zs.talk(message);
            }else if(xwj.equals(person)){
                xwj.talk(message);
            }
    }
}

最後再來進行測試,定義好交流平臺以及需要交流的人員。
那麼測試代碼如下:


public static void main(String[] args) {
    JavaQQqun jq = new JavaQQqun();  
       ZhangSan zs = new ZhangSan("張三", jq);
       XuWuJing xwj = new XuWuJing("xuwujing", jq);
       jq.setZs(zs);
       jq.setXwj(xwj);      
       zs.talk("大家好!我是張三!");;
       xwj.talk("歡迎你!張三!");

}

輸出結果:

張三說:大家好!我是張三
xuwujing回應:歡迎你!張三!

中介者模式優點:

靈活性高,因為將同事類進行瞭解耦,使其不必有關聯性;
降低了類的複雜度,將一對多轉化成了一對一;

中介者模式缺點:

中介者使用過多,會使系統變得複雜難以維護;

使用場景:

通過一個中間類來封裝多個類中的行為,而又不想生成太多的子類。

註意事項:

若不明確各個類的職責,那麼就不要進行使用!

和外觀模式、代理模式比較

中介者模式和外觀模式、代理模式比較類似,但是又有不同。
和外觀模式比較,中介者模式中,同事類必須依賴與中介者,中介者也知道同事類;但是外觀模式中,子系統是不需要知道外觀類的存在,並且子系統是可以脫離外觀模式的。
和代理模式,代理模式的核心就是代理作用,主要還是對原先的類進行擴展或增加控制,比如進行許可權控制;而中介者模式主要目的是為了減少對象之前的耦合,也就是同事類直接相互獨立,互不影響。


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

-Advertisement-
Play Games
更多相關文章
  • js總是可以在不知不覺中地創建了全局變數,其原因在於JavaScript的兩個特性。 1.JavaScript可直接使用變數,甚至無需聲明; 2.JavaScript有個暗示全局變數的概念,即任何變數,如果未經聲明,就為全局對象所有(也就像正確聲明過的全局變數一樣可以訪問)。 在這個例子中,resu ...
  • 1、頁面定製CSS代碼 2、博客側邊欄公告(支持HTML代碼)(支持JS代碼) 3、設置網易雲外鏈 打開網易雲音樂網站,網頁版的 找自己喜歡的音樂活著歌單 博客園不支持iframe,選擇flash插件 自己選擇是否自動播放,由於我選的曲子很。。。有活力,所以我是不自動播放的。 之後複製代碼覆蓋這裡: ...
  • ...
  • 在使用scss是可能會添加loader,例如 然而當使用vue-cli腳手架創建項目時,可能出現如下錯誤: 這是loader重覆導致的,在build/utils.js中的exports.cssLoaders中已經返回相關loader了, 解決方案很簡單: 就是自己不要加樣式相關的loader了;或者 ...
  • 即時通訊:支持好友,群組,發圖片、文件,消息聲音提醒,離線消息,保留聊天記錄 (即時聊天功能支持手機端,詳情下麵有截圖) 工作流模塊 1.模型管理 :web線上流程設計器、預覽流程xml、導出xml、部署流程 2.流程管理 :導入導出流程資源文件、查看流程圖、根據流程實例反射出流程模型、激活掛起 3 ...
  • 定義保證一個類僅有一個實例,並提供一個該實例的全局訪問點。 --《設計模式GoF》UML類圖使用場景當類只能有一個實例並且用戶可以從一個眾所周知的訪問點訪問它時。創建一個對象需要消耗過多的資源,比如IO和資料庫連接等。C#代碼實現1,初始版本namespace DesignPatternDemo.C... ...
  • RabbitMQ是一個開源的AMQP實現,伺服器端用Erlang語言編寫,支持多種客戶端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用於在分散式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。 簡 ...
  • 思路 用$f[i]$表示完成第$i$棵子樹所需要得時間。 考慮如果有兩個子樹$a$和$b$,如果先去完成子樹$a$,那麼對於花費得時間就是$f[b] + siz[a] \times 2 + 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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...