深入理解設計模式:訪問者模式詳解

在軟件開發中,我們經常會遇到需要對一個復雜對象結構進行操作的情況。隨著需求的不斷變化,我們可能需要在這個對象結構上添加各種新的操作。如果直接在對象結構中添加這些操作,會導致類的職責過重,且每次添加新操作都需要修改原有代碼,違反了開閉原則。訪問者模式(Visitor Pattern)就是為了解決這類問題而誕生的。

本文將全面介紹訪問者模式,包括其定義、結構、實現方式、優缺點、適用場景以及在實際項目中的應用案例,幫助讀者深入理解這一重要的設計模式。

一、訪問者模式概述

1.1 模式定義

訪問者模式是一種行為型設計模式,它允許你將算法與對象結構分離,使得可以在不修改現有對象結構的情況下向對象結構添加新的操作。該模式的核心思想是將數據結構與數據操作分離,從而達到解耦的目的。

1.2 模式特點

訪問者模式具有以下顯著特點:

  • 將相關操作集中到一個訪問者對象中,而不是分散在元素類中

  • 可以方便地添加新的操作,只需增加新的訪問者類即可

  • 訪問者可以累積狀態,這在遍歷復雜對象結構時很有用

1.3 模式起源

訪問者模式最早由Gang of Four(Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides)在他們1994年出版的《設計模式:可復用面向對象軟件的基礎》一書中提出,是23種經典設計模式之一。

二、訪問者模式的結構

2.1 UML類圖

2.2 模式角色

訪問者模式包含以下主要角色:

  1. Visitor(訪問者接口)

    • 聲明了一組訪問操作,每個操作對應一個具體元素類

    • 通常為每個具體元素類聲明一個visit方法

  2. ConcreteVisitor(具體訪問者)

    • 實現Visitor接口聲明的操作

    • 每個操作實現算法的一部分,而該算法片段對應于結構中的相應類

  3. Element(元素接口)

    • 定義一個accept方法接受訪問者對象

    • 通常是"public void accept(Visitor visitor)"

  4. ConcreteElement(具體元素)

    • 實現Element接口的accept方法

    • 在accept方法中調用訪問者的visit方法

  5. ObjectStructure(對象結構)

    • 能夠枚舉它的元素

    • 可以提供一個高層接口允許訪問者訪問它的元素

    • 可以是一個組合模式或是一個簡單的集合

2.3 模式協作

訪問者模式的工作流程如下:

  1. 客戶端創建一個具體訪問者對象

  2. 客戶端通過對象結構的接口遍歷所有元素

  3. 客戶端將訪問者對象傳遞給每個元素的accept操作

  4. 元素的accept操作調用訪問者的visit操作,并將自身作為參數傳遞

三、訪問者模式的實現

3.1 基礎實現示例

下面是一個完整的Java實現示例:

// 訪問者接口
interface Visitor {void visit(ConcreteElementA element);void visit(ConcreteElementB element);
}// 具體訪問者1
class ConcreteVisitor1 implements Visitor {public void visit(ConcreteElementA element) {System.out.println("ConcreteVisitor1處理ConcreteElementA: " + element.operationA());}public void visit(ConcreteElementB element) {System.out.println("ConcreteVisitor1處理ConcreteElementB: " + element.operationB());}
}// 具體訪問者2
class ConcreteVisitor2 implements Visitor {public void visit(ConcreteElementA element) {System.out.println("ConcreteVisitor2處理ConcreteElementA: " + element.operationA());}public void visit(ConcreteElementB element) {System.out.println("ConcreteVisitor2處理ConcreteElementB: " + element.operationB());}
}// 元素接口
interface Element {void accept(Visitor visitor);
}// 具體元素A
class ConcreteElementA implements Element {public void accept(Visitor visitor) {visitor.visit(this);}public String operationA() {return "具體元素A的操作";}
}// 具體元素B
class ConcreteElementB implements Element {public void accept(Visitor visitor) {visitor.visit(this);}public String operationB() {return "具體元素B的操作";}
}// 對象結構
class ObjectStructure {private List<Element> elements = new ArrayList<>();public void attach(Element element) {elements.add(element);}public void detach(Element element) {elements.remove(element);}public void accept(Visitor visitor) {for (Element element : elements) {element.accept(visitor);}}
}// 客戶端代碼
public class Client {public static void main(String[] args) {ObjectStructure objectStructure = new ObjectStructure();objectStructure.attach(new ConcreteElementA());objectStructure.attach(new ConcreteElementB());Visitor visitor1 = new ConcreteVisitor1();objectStructure.accept(visitor1);System.out.println("----------");Visitor visitor2 = new ConcreteVisitor2();objectStructure.accept(visitor2);}
}

3.2 輸出結果

ConcreteVisitor1處理ConcreteElementA: 具體元素A的操作
ConcreteVisitor1處理ConcreteElementB: 具體元素B的操作
----------
ConcreteVisitor2處理ConcreteElementA: 具體元素A的操作
ConcreteVisitor2處理ConcreteElementB: 具體元素B的操作

3.3 實現要點

  1. 雙分派機制:訪問者模式使用了雙分派(double dispatch)的技術,即根據請求的類型和接收者的類型來決定使用哪個方法

  2. 元素接口設計:Element接口應該足夠通用,以支持各種訪問者

  3. 訪問者接口設計:Visitor接口應該為每種具體元素類型都聲明一個visit方法

  4. 對象結構管理:ObjectStructure負責維護元素集合,并提供遍歷接口

四、訪問者模式的優缺點

4.1 優點

  1. 開閉原則:可以在不修改現有對象結構的情況下添加新的操作,只需增加新的訪問者類

  2. 單一職責原則:將相關行為集中到一個訪問者對象中,而不是分散在元素類中

  3. 靈活性:訪問者可以累積狀態,這在遍歷復雜對象結構時很有用

  4. 復用性:可以在不同訪問者中復用相同的元素結構

  5. 擴展性:添加新的訪問者很容易,不需要修改元素類

4.2 缺點

  1. 破壞封裝:訪問者可能需要訪問元素的內部狀態,這可能會破壞元素的封裝性

  2. 元素接口變更困難:每增加一個新的具體元素類,都需要修改訪問者接口及所有具體訪問者類

  3. 對象結構變更困難:如果對象結構經常變化,訪問者模式可能不太適用

  4. 復雜性增加:對于簡單的對象結構,使用訪問者模式可能會增加不必要的復雜性

五、訪問者模式的適用場景

訪問者模式適用于以下場景:

  1. 復雜對象結構:對象結構中包含許多具有不同接口的對象類,且希望對它們執行依賴于具體類的操作

  2. 多種不相關操作:需要對一個對象結構中的對象進行很多不同且不相關的操作,且不希望這些操作"污染"元素的類

  3. 穩定的數據結構:對象結構很少變化,但經常需要在此結構上定義新的操作

  4. 算法與結構分離:希望將算法與它們操作的對象結構分離

六、訪問者模式的實際應用

6.1 編譯器設計

在編譯器設計中,抽象語法樹(AST)是一個典型的復雜對象結構。訪問者模式可以用于實現各種AST操作,如類型檢查、代碼優化、代碼生成等。每種操作可以由不同的訪問者實現,而不需要修改AST節點類。

// AST節點接口
interface ASTNode {void accept(ASTVisitor visitor);
}// 訪問者接口
interface ASTVisitor {void visit(AssignmentNode node);void visit(VariableNode node);void visit(NumberNode node);
}// 類型檢查訪問者
class TypeCheckVisitor implements ASTVisitor {public void visit(AssignmentNode node) {// 類型檢查邏輯}public void visit(VariableNode node) {// 類型檢查邏輯}public void visit(NumberNode node) {// 類型檢查邏輯}
}// 代碼生成訪問者
class CodeGenVisitor implements ASTVisitor {public void visit(AssignmentNode node) {// 代碼生成邏輯}public void visit(VariableNode node) {// 代碼生成邏輯}public void visit(NumberNode node) {// 代碼生成邏輯}
}

6.2 文檔處理

在XML或JSON文檔處理中,可以使用訪問者模式來實現各種文檔處理操作,如格式轉換、內容提取、驗證等。

6.3 GUI組件處理

在圖形用戶界面中,復雜的UI組件樹可以使用訪問者模式來實現各種操作,如渲染、布局計算、事件處理等。

七、訪問者模式與其他模式的關系

  1. 組合模式:訪問者模式經常用于處理由組合模式定義的對象結構

  2. 解釋器模式:訪問者可以用于在抽象語法樹上執行操作

  3. 迭代器模式:訪問者模式可以看作是一個更復雜的迭代器,它不僅遍歷對象結構,還對每個元素執行操作

八、總結

訪問者模式是一種強大的行為設計模式,它通過將算法與對象結構分離,提供了在不修改現有類的情況下擴展其功能的能力。雖然它有一些缺點,如可能破壞封裝性和增加系統復雜性,但在適當的場景下,訪問者模式可以極大地提高代碼的靈活性和可維護性。

在實際應用中,訪問者模式特別適合于那些數據結構穩定但操作頻繁變化的系統。當我們需要對復雜對象結構執行多種不相關的操作時,訪問者模式可以有效地組織代碼,避免"操作污染"元素類。

理解并合理運用訪問者模式,可以幫助我們設計出更加靈活、可擴展的軟件系統。

?

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

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

相關文章

Linux timerfd 定時器封裝

使用 timerfd epoll() 實現&#xff0c;簡潔精確。沒定義 MU_ERROR 宏的話替換為 printf 即可。mu_timer.h:#ifndef _MU_TIMER_H_ #define _MU_TIMER_H_#ifdef __cplusplus extern "C" { #endif#include <stdint.h> #include <time.h> #include <pth…

【樣式效果】Vue3實現仿制iOS按鈕動態效果

iOS開關效果定義變量&#xff1a; <style scoped lang"scss">.layout {// 按鈕寬度$button-width: 500px;// 按鈕高度$button-height: 250px;// 按鈕里面圓形直徑$circle-diameter: 200px;// 按鈕背景與里面圓形間距$button-circle-offset:calc(($button-he…

京東瘋狂投資具身智能:眾擎機器人+千尋智能+逐際動力 | AI早報

每日分享全球最新AI資訊【應用商業八卦技術】&#xff0c;&#x1f30f;&#xff1a;未來世界2099應用 1、馬斯克推出兒童AI"Baby Grok"引熱議&#xff1a;安全性能否經受考驗&#xff1f; 2、螞蟻AQ健康應用霸榜蘋果商店&#xff0c;或將聯手Apple Watch打造智能健康…

Jiasou TideFlow AIGC SEO Agent:全自動外鏈構建技術重構智能營銷新標準

AI時代SEO技術革命&#xff1a;企業如何突破流量增長瓶頸&#xff1f;隨著Google算法升級至MUM模型&#xff0c;傳統SEO工具已難以應對多模態內容優化需求。在搜索引擎日均處理120億次查詢的生態中&#xff0c;企業官網平均自然流量轉化周期長達6-8個月&#xff0c;因此諸如Jia…

Docker-compose:服務編排

Docker-compose 介紹 服務編排:按照一定的業務規則批量管理容器 在微服務架構的應用系統中,一般包含 N 個微服務,且每個微服務一般都會部署多個實例。此時,如果每個微服務都要手動啟停,維護的工作量會很大。 要從 Dockerfile build image 或者去 docker hub 拉取 image …

異地服務器備份Mysql數據

前言異地服務器備份Mysql數據即Mysql的server端與備份服務器不是同一個。Mysql服務端安裝在192.168.3.36中&#xff0c;現在需要在IP為192.168.209.129的服務器中使用mysqldump命令備份指定數據庫數據;192.168.209.129沒有裝過Mysql客戶端;1.安裝Mysql客戶端不安裝Mysql客戶端就…

NGINX 高級配置解析:`proxy_request_buffering` 使用詳解

在使用 NGINX 作為反向代理服務器時&#xff0c;處理客戶端請求體&#xff08;如上傳文件或大體積 POST 請求&#xff09;的方式會直接影響應用性能與資源使用。其中&#xff0c;proxy_request_buffering 是一個非常關鍵但容易被忽略的配置項。 本文將詳細介紹該指令的作用、典…

增加交叉驗證和超參數調優

前文中&#xff0c;只是給了基礎模型&#xff1a; PyTorch 實現 CIFAR-10 圖像分類&#xff1a;從數據預處理到模型訓練與評估-CSDN博客 今天我們增加交叉驗證和超參數調優&#xff0c; 先看運行結果&#xff1a; 在測試集上評估最終模型 最終模型在測試集上的準確率&…

解決pip指令超時問題

用pip指令&#xff0c;在安裝Django3.2時報錯&#xff0c;詢問ChatGpt后得到的解決方案pip 下載超時 —— 是 當前網絡連接到 PyPI 官方源太慢或不穩定&#xff0c;甚至可能連不上了&#xff0c;而 pip 默認的超時時間又太短&#xff0c;就導致了中途失敗&#xff1a;ReadTimeo…

Oracle定時清理歸檔日志

線上歸檔日志滿了&#xff0c;系統直接崩了&#xff0c;為解決這個問題&#xff0c;創建每月定時清理歸檔日志。 創建文件名 delete_archivelog.rman CONFIGURE ARCHIVELOG DELETION POLICY CLEAR; RUN {ALLOCATE CHANNEL c1 TYPE DISK;DELETE ARCHIVELOG ALL COMPLETED BEFORE…

ELF 文件操作手冊

目錄 一、ELF 文件結構概述 二、查看 ELF 文件頭信息 1、命令選項 2、示例輸出 3、內核數據結構 三、ELF 程序頭表 1、命令選項 2、示例輸出 3、關鍵說明 4、內核數據結構 四、ELF 節頭表詳解 查看節頭表信息 1、命令選項 2、示例輸出 3、標志說明 4、重要節說…

深入淺出Python函數:參數傳遞、作用域與案例詳解

&#x1f64b;?♀? 博主介紹&#xff1a;顏顏yan_ ? 本期精彩&#xff1a;深入淺出Python函數&#xff1a;參數傳遞、作用域與案例詳解 &#x1f3c6; 熱門專欄&#xff1a;零基礎玩轉Python爬蟲&#xff1a;手把手教你成為數據獵人 &#x1f680; 專欄亮點&#xff1a;零基…

ps aux 和 ps -ef

在 Linux/Unix 系統中&#xff0c;ps aux 和 ps -ef 都是用于查看進程信息的命令&#xff0c;結合 grep node 可以篩選出與 Node.js 相關的進程。它們的核心功能相似&#xff0c;但在輸出格式和選項含義上有區別&#xff1a;1. 命令對比命令含義主要區別ps auxBSD 風格語法列更…

Spark ML 之 LSH

src/test/scala/org/apache/spark/ml/feature/BucketedRandomProjectionLSHSuite.scala test("approxSimilarityJoin for self join") {val data = {for (i <- 0 until 24) yield Vectors

關鍵成功因素法(CSF)深度解析:從戰略目標到數據字典

關鍵成功因素法由John Rockart提出&#xff0c;用于信息系統規劃&#xff0c;幫助企業識別影響系統成功的關鍵因素&#xff0c;從而確定信息需求&#xff0c;指導信息技術管理。該方法通過識別關鍵成功因素&#xff0c;找出關鍵信息集合&#xff0c;確定系統開發優先級&#xf…

Django母嬰商城項目實踐(六)- Models模型之ORM操作

6、Models模型操作 1 ORM概述 介紹 Django對數據進行增刪改操作是借助內置的ORM框架(Object Relational Mapping,對象關系映射)所提供的API方法實現的,允許你使用類和對象對數據庫進行操作,從而避免通過SQL語句操作數據庫。 簡單來說,ORM框架的數據操作API是在 QuerySet…

【PTA數據結構 | C語言版】哥尼斯堡的“七橋問題”

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 哥尼斯堡是位于普累格河上的一座城市&#xff0c;它包含兩個島嶼及連接它們的七座橋&#xff0c;如下圖所示。 可否走過這樣的七座橋&#xff0c;而且每橋只走過一次&#xff1f;瑞士數學家歐拉(Leo…

Redis 詳解:從入門到進階

文章目錄前言一、什么是 Redis&#xff1f;二、Redis 使用場景1. 緩存熱點數據2. 消息隊列3. 分布式鎖4. 限流與防刷5. 計數器、排行榜三、緩存三大問題&#xff1a;雪崩 / 穿透 / 擊穿1. ?? 緩存雪崩&#xff08;Cache Avalanche&#xff09;2. &#x1f50d; 緩存穿透&…

QCustomPlot 使用教程

下載網址&#xff1a;官方網站&#xff1a;http://www.qcustomplot.com/我的環境是 window10 qt5.9.9 下載后&#xff0c;官網提供了很多例子。可以作為參考直接運行自己如何使用&#xff1a;第一步&#xff1a;使用QCustomPlot非常簡單&#xff0c;只需要把qcustomplot.cpp和…

基于springboot+mysql的作業管理系統(源碼+論文)

一、開發環境 1 Spring Boot框架簡介 描述&#xff1a; 簡化開發&#xff1a;Spring Boot旨在簡化新Spring應用的初始搭建和開發過程。配置方式&#xff1a;采用特定的配置方式&#xff0c;減少樣板化配置&#xff0c;使開發人員無需定義繁瑣的配置。開發工具&#xff1a;可…