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

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

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



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


模式動機

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

在 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集中到一個訪問者對象中,而不是分散到一個個元素類,類的職責更加清晰
  • 可以跨過類的等級結構訪問不同等級結構的元素類
  • 用戶能夠在不修改現有類層次結構的情況下,定義該類層次結構的新操作

訪問者模式的缺點:

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


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

-Advertisement-
Play Games
更多相關文章
  • 面對這麼多的知識點,有的盆友就麻爪了…… 我是誰? 我該從哪裡開始看? 我該怎麼看? 我該看多少? 這,是一個問題。 我們貼心的做了一個學習線路圖: 然並卵,很多人還是一頭霧水…… 我們先對每階段課程做個簡單介紹: 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源碼前,有必要花一定時間瞭解設計模式。 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...