軟體設計模式學習(二十七)訪問者模式

来源:https://www.cnblogs.com/Yee-Q/archive/2020/06/18/13157842.html

訪問者模式是一種較為複雜的行為型設計模式,它包含訪問者和被訪問元素兩個主要組成部分,這些被訪問的元素具有不同的類型,且不同的訪問者可以對其進行不同的訪問操作 模式動機 對於系統中某些對象,它們存儲在同一個集合中,且具有不同的類型。對於該集合中的對象,可以接受一類稱為訪問者的對象來訪問,不同的訪問者其 ...



訪問者模式是一種較為複雜的行為型設計模式,它包含訪問者和被訪問元素兩個主要組成部分,這些被訪問的元素具有不同的類型,且不同的訪問者可以對其進行不同的訪問操作


模式動機

對於系統中某些對象,它們存儲在同一個集合中,且具有不同的類型。對於該集合中的對象,可以接受一類稱為訪問者的對象來訪問,不同的訪問者其訪問方式有所不同。

在 Java 等面向對象語言中都提供了大量用於存儲多個元素的集合對象,集合中存儲的對象有時候是同一類型,有時候不是同一類型,或許它們只是具有共同的父類。假如我們要針對一個包含不同類型元素的集合採取某種操作,而操作細節根據元素類型不同而不同,就會出現大量類型判斷語句,增大代碼複雜度。

實際使用時,對相同元素的對象也可能存在多種不同的操作方式,而且可能還需要增加新的操作,此時訪問者模式是一個值得考慮的解決方案。


模式定義

表示一個作用於某對象結構中的各元素的操作,它使我們可以在不改變各元素的類的前提下定義作用於這些元素的新操作。訪問者模式是一種對象行為型模式。

Represent an operation to be performedd on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.


模式分析

訪問者模式結構較為複雜,首先看一張模式結構類圖

對象結構(ObjectStructure)是一個元素集合,存儲了不同類型的元素對象,以供不同訪問者訪問。訪問者模式包括兩個層次,一個是訪問者層次結構,另一個是元素層次結構。

訪問者層次結構提供了抽象訪問者(Visitor)和具體訪問者(ConcreteVisitor)。抽象訪問者聲明瞭訪問元素對象的方法,通常為每一種類型的元素對象都提供一個訪問方法,而具體訪問者可以實現這些訪問方法。

這些訪問方法的設計又有兩種,一種是直接在方法名中標明待訪問元素對象的類型,如 visitConcreteElementA(ConcreteElementA elementA),還有一種是統一取名為 visit(),通過參數類型的不同來定義一系列重載方法。

public abstract class Visitor {
    // 統一取名
    public abstract void visit(ConcreteElementA elementA);
    public abstract void visit(ConcreteElementB elementB);
    // 如果所有訪問者對某一類型的元素訪問操作都相同
    // 則可以將操作代碼移到抽象訪問者中
    public void visit(ConcreteElementC elementC) {
        ...
    }
}

元素層次結構提供了抽象元素類(Element)和具體元素類(ConcreteElementA),抽象元素類一般都聲明一個 accept() 方法,用於接受訪問者的訪問。該方法傳入一個抽象訪問者 Visitor 類型的參數,在程式運行時確定其具體訪問者的類型,並調用具體訪問者對象的 visit() 方法實現對元素對象的操作

public class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public void operationA() {
        // 在具體元素類中可以定義不同類型的元素所特有的業務方法
    }
}

具體元素類 ConcreteElementA 的 accept() 方法通過調用 Visitor 類的 visit() 方法實現對元素的訪問,並以當前對象作為 visit() 方法的參數,這種調用機制也稱“雙重分派”。正因為使用了雙重分派技術,使得增加新的訪問者無須修改現有類庫代碼,只需將新的訪問者對象傳入具體元素對象的 accept() 方法即可,程式運行時將回調在 Visitor 類中定義的 visit() 方法,從而實現不同形式的訪問。

對象結構(ObjectStructure)是一個集合,用於存儲元素對象並接受訪問者的訪問。在對象結構中可以使用迭代器對存儲在集合中的元素對象進行遍歷,並逐個調用每一個對象的 accept() 方法,實現對元素對象的訪問操作。

public class ObjectStructure {
    
    private ArrayList list = new ArrayList();
    
    public void accept(Visitor visitor) {
        Iterator i = list.iterator();
        while(i.hashNext()) {
            ((Element)i.next()).accept(visitor);
        }
    }
    
    public void addElement(Element element) {
		list.add(element);
    }
    
    public void removeElement(Element element) {
        list.remove(element);
    }
}

最終在客戶端我們需要實例化一個對象結構對象,並向其添加元素對象,再調用 accept() 方法來接受訪問者對象的訪問。具體訪問者類型可以通過配置文件來確定。

public class Client {
    
    public static void main(String[] args) {
        Element elementA = new ElementA();
        Element elementB = new ElementB();
        Element elementC = new ElementC();
        
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.addElement(elementA);
        objectStructure.addElement(elementB);
        objectStructure.addElement(elementC);
        
        Visitor visitor = new ConcreteVisitorA();
        objectStructure.accept(visitor);
    }
}

如果需要修改訪問者類型,只或者增加新的類型的訪問者,只需修改配置文件即可,符合開閉原則。但如果要增加新的類型的具體元素類,則訪問者類需要為其定義新的訪問方法,從這一點看又違背了開閉原則。


模式優缺點

訪問者模式的優點:

  • 使得增加新的訪問操作變得容易,無須修改現有類庫的代碼
  • 將有關類對象的訪問行為i集中到一個訪問者對象中,而不是分散到一個個元素類,類的職責更加清晰
  • 可以跨過類的等級結構訪問不同等級結構的元素類
  • 用戶能夠在不修改現有類層次結構的情況下,定義該類層次結構的新操作

訪問者模式的缺點:

  • 增加新的元素類很困難
  • 訪問者模式要求訪問者對象訪問並調用每一個元素對象的操作,破壞了封裝性


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

更多相關文章
  • 面對這麼多的知識點,有的盆友就麻爪了…… 我是誰? 我該從哪裡開始看? 我該怎麼看? 我該看多少? 這,是一個問題。 我們貼心的做了一個學習線路圖: 然並卵,很多人還是一頭霧水…… 我們先對每階段課程做個簡單介紹: PC端頁面製作 學習HTML+CSS搭建網頁、PhotoShop切圖等基礎知識,屬於 ...
  • 前言現在的 Node 對於前端而言可以涵蓋各個方面,包括命令行介面、插件、依賴庫、腳手架以及 Web 服務等。本文是一篇對於 Node 使用的淺談文章,會簡單講解一些個人使用 Node 的經驗,分享的內容主要可分為三個方面: 工具篇 插件篇 服務篇 工具篇會講解使用 NPM 發佈命令行介面的簡單教程 ...
  • 前言 1.本文將從零開始手寫一份vue-next中的響應式原理,出於篇幅和理解的難易程度,我們將只實現核心的api並忽略一些邊界的功能點 本文將實現的api包括 track trigger effect reactive watch computed2.最近很多人私信我問前端問題,博客登陸的少沒及時 ...
  • multipage Github地址 github.com/qinouz/mult…基於 vue-cli4.0 構建 多頁面 模板腳手架! 啟動項目 git clone https://github.com/qinouz/multipage.git cd multipage npm install ...
  • 遇到問題 數據源有數據,但表體無法渲染出數據。 排查問題之路 其實關於layui表格無法渲染的問題之前也遇到過,我知道的情況以下幾種 數據源返回的的格式不正確 (code 必須為 0 ) 正確的返回格式:{"code":0,"msg":"","count":1000,"data":[]} 數據源正確 ...
  • 轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 原文出處:https://dzone.com/articles/why-masses-are-not-using-latest-css-features-in-20 儘管CSS每年都會發佈全新的特性,但實際上 ...
  • 6.1 webpack概念的引入 在網頁中會引用哪些常見的靜態資源? JS .js、 .jsx 、.coffee、 .ts(TypeScript 類 C# 語言) CSS .css、 .less、 .sass 、.scss Images .jpg 、.png、 .gif 、.bmp 、.svg 字體 ...
  • 常用設計模式思維導圖,大部分設計模式在spring和jdk源碼中都有體現。 在學習spring源碼前,有必要花一定時間瞭解設計模式。 ...
一周排行
  • C#6.0新特性 C#7.0新特性 C#8.0新特性 ...
  • out變數 可以直接在方法中使用out申明變數 int.TryParse("123", out var result); 元組 元組的申明 var alphaBetaStart = (alpha: "a", beta: "b"); Console.WriteLine($"{alphaBetaStar ...
  • 在我們的項目中,通常會把數據存儲到關係型資料庫中,比如Oracle,SQL Server,Mysql等,但是關係型資料庫對於併發的支持並不是很強大,這樣就會造成系統的性能不佳,而且存儲的數據多為結構化數據,對於非結構數據(比如文本)和半結構化數據(比如JSon) 就顯得不夠靈活,而非關係型資料庫則很 ...
  • 這幾天終於弄懂了async和await的模式,也搞明白了一直在心裡面積壓著的許多問題,所以寫一篇博客來和大家分享一下。 關於非同步機制我認為只要記住的以下幾點,就可以弄明白了: 1.我認為async和awwait兩個修飾符中最關鍵的是await,async是由於方法中包含await修飾符之後才在方法定 ...
  • 實現WCF的步驟如下: 設計服務協議 實現服務協議 配置服務 托管服務 生成客戶端(這步可有可無) 設計或定義服務協議要麼使用介面,要麼使用類。建議介面,使用介面好處一堆例如修改介面的實現,但是服務協定有無需改變。 設計服務協議,介面上使用 ServiceContractAttribute ,方法上 ...
  • 什麼鬼,我的CPF快寫好了,你居然也要搞跨平臺UI框架?什麼Maui? 之前怎麼不早說要搞跨平臺UI框架呢?看到谷歌搞flutter眼紅了?明年年底發佈?又搞這種追別人屁股的爛事情。 什麼MVU模式?模仿Dart?用C#代碼直接寫UI的模式和我的CPF很像啊。 當初我考慮過XML,Json來描述UI ...
  • 寫在前面 Docker作為開源的應用容器引擎,可以讓我們很輕鬆的構建一個輕量級、易移植的容器,通過Docker方式進行持續交付、測試和部署,都是極為方便的,並且對於我們開發來說,最直觀的優點還是解決了日常開發中的環境配置與部署環境配置上的差異所帶來的種種疑難雜症,從此推脫產品的措辭也少了——“我電腦 ...
  • 一、前言 回顧:認證授權方案之授權初識 從上一節中,我們在對授權系統已經有了初步的認識和使用,可以發現,asp.net core為我們提供的授權策略是一個非常強大豐富且靈活的認證授權方案,能夠滿足大部分的授權場景。 在ConfigureServices中配置服務:將授權服務添加到容器 public ...
  • 項目背景: 工作之餘兼職一家公司(方向是工業4.0)給做IM系統,主要功能包括:文字、 圖片、文件傳輸、遠程協助、視頻語音等等。這些功能都是基於群會話, 比如工廠操作工人遇到問題,請求遠程專家,這個初級專家不能解決問題,會邀請一個高級專家進來解決。開發過程中主要遇到的問題是視頻和語音這一塊,像其他的... ...
  • 基礎概念 Microsoft中間語言(MSIL),也成為通用中間語言(CIL),是一組與平臺無關的指令,由特定於語言的編譯器從源代碼生成。MSIL是獨立於平臺的,因此,他可以在任何公共語言基礎架構支持特定的環境上執行。 通過JIT編譯器將MSIL轉換為特定電腦環境的特定機器代碼。這是在執行MSIL ...