訪問者模式 (Visitor Pattern)

定義

訪問者模式(Visitor Pattern)是一種行為型設計模式,用于將算法與其作用于的對象結構分離。這種模式主要用于執行操作或應用過程,這些操作需要在不同類型的對象上執行,同時避免讓這些對象的類變得過于復雜。

關鍵組成部分

  1. 訪問者(Visitor)
    • 一個接口或抽象類,定義了對不同類型元素(Element)的訪問操作。
    • 實現了每種類型元素的操作,是將操作邏輯從元素類中分離出來的關鍵所在。
  2. 元素(Element)
    • 定義了一個接受訪問者的方法(通常為 accept),該方法允許訪問者對象訪問元素。
    • 元素結構通常穩定,且含有多個接受訪問的方法,每個方法對應一種類型的訪問者。
  3. 具體元素(Concrete Element)
    • 實現元素接口,定義了 accept 方法的具體實現。
    • 可能有多個不同類型的具體元素類,每個類都有自己的邏輯和接受訪問者的方式。
  4. 具體訪問者(Concrete Visitor)
    • 實現訪問者接口,定義了對每個元素類的具體操作。
    • 可能有多個不同類型的具體訪問者,每個訪問者都實現了一套作用于元素的操作。
解決的問題
  • 操作與對象結構的分離:在復雜對象結構中,經常需要執行各種不依賴于特定對象的操作。訪問者模式使得可以將這些操作從對象結構中分離出來,以減少這些操作對于對象結構的影響。
  • 添加新操作的靈活性:當新的操作需要在這些對象上執行時,你可能不希望更改這些對象的類。訪問者模式允許你通過添加新的訪問者類來添加新的操作,而無需修改對象的類。
  • 集中相關操作:在傳統的面向對象設計中,相關的操作可能分散在各個類中。訪問者模式允許你將相關操作集中在一個訪問者類中,這樣可以避免在對象結構中散布這些操作,從而提高代碼的組織性和可維護性。
  • 擴展性:對于那些可能需要添加新操作的對象結構,訪問者模式提供了一種容易擴展的方式。你可以在不更改現有代碼的情況下,通過創建新的訪問者來添加新的操作。
  • 聚合操作:在一些情況下,你可能需要對一個復雜的對象結構執行聚合操作,如遍歷、搜索或生成報告。訪問者模式使得這些操作可以被集中管理和維護。
使用場景
  • 復雜對象結構:在復雜的對象結構中(如樹狀或圖狀結構),需要對結構中的各個對象執行操作,而這些操作依賴于對象的具體類型。訪問者模式允許在不修改這些對象類的情況下,添加新的操作。
  • 添加新操作:當需要對一個對象結構添加新的操作,且不希望這些操作影響到對象的類時。訪問者模式允許將操作邏輯封裝在訪問者中,易于擴展。
  • 避免"污染"對象類:如果在每個對象類中添加新操作會導致類變得復雜或不易維護,那么使用訪問者模式將這些操作外部化是一個好選擇。
  • 不同的訪問者實現不同的操作:當同一個對象結構需要支持多種不同的操作,且這些操作是互相獨立的。例如,可能有一個用于渲染對象的訪問者,另一個用于檢查對象的完整性。
  • 頻繁變更的操作:如果一組操作經常變更,但對象結構相對穩定,那么將這些操作作為訪問者的一部分,可以避免頻繁修改對象結構。
  • 累積狀態:在遍歷一個復雜結構時,如果需要在訪問者中累積狀態,而不是在元素中累積,那么訪問者模式也是一個不錯的選擇。
示例代碼1-計算機部件訪問者

在這個例子中,我們定義了一個計算機部件(ComputerPart)的接口和一些具體部件類(Keyboard、Monitor、Mouse),以及一個訪問者接口(ComputerPartVisitor)和一個具體的訪問者實現(ComputerPartDisplayVisitor)。

// 訪問者接口
interface ComputerPartVisitor {void visit(Computer computer);void visit(Mouse mouse);void visit(Keyboard keyboard);void visit(Monitor monitor);
}// 元素接口
interface ComputerPart {void accept(ComputerPartVisitor computerPartVisitor);
}// 元素實現
class Keyboard implements ComputerPart {public void accept(ComputerPartVisitor computerPartVisitor) {computerPartVisitor.visit(this);}
}class Monitor implements ComputerPart {public void accept(ComputerPartVisitor computerPartVisitor) {computerPartVisitor.visit(this);}
}class Mouse implements ComputerPart {public void accept(ComputerPartVisitor computerPartVisitor) {computerPartVisitor.visit(this);}
}class Computer implements ComputerPart {ComputerPart[] parts;public Computer(){parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};		}public void accept(ComputerPartVisitor computerPartVisitor) {for (int i = 0; i < parts.length; i++) {parts[i].accept(computerPartVisitor);}computerPartVisitor.visit(this);}
}// 具體訪問者
class ComputerPartDisplayVisitor implements ComputerPartVisitor {public void visit(Computer computer) {System.out.println("Displaying Computer.");}public void visit(Mouse mouse) {System.out.println("Displaying Mouse.");}public void visit(Keyboard keyboard) {System.out.println("Displaying Keyboard.");}public void visit(Monitor monitor) {System.out.println("Displaying Monitor.");}
}// 客戶端
public class VisitorPatternDemo {public static void main(String[] args) {ComputerPart computer = new Computer();computer.accept(new ComputerPartDisplayVisitor());}
}
示例代碼2-媒體文件的操作

在這個例子中,我們有不同類型的媒體文件(如音頻文件和視頻文件),并希望執行不同的操作,例如播放和編碼。我們將定義媒體文件的接口和具體類,以及一個訪問者接口和兩個具體的訪問者實現。

// 媒體文件接口
interface MediaFile {void accept(MediaFileVisitor visitor);
}// 音頻文件
class AudioFile implements MediaFile {private String filename;public AudioFile(String filename) {this.filename = filename;}public String getFilename() {return filename;}@Overridepublic void accept(MediaFileVisitor visitor) {visitor.visit(this);}
}// 視頻文件
class VideoFile implements MediaFile {private String filename;public VideoFile(String filename) {this.filename = filename;}public String getFilename() {return filename;}@Overridepublic void accept(MediaFileVisitor visitor) {visitor.visit(this);}
}// 媒體文件訪問者接口
interface MediaFileVisitor {void visit(AudioFile audio);void visit(VideoFile video);
}// 播放操作訪問者
class PlayVisitor implements MediaFileVisitor {@Overridepublic void visit(AudioFile audio) {System.out.println("Playing audio file: " + audio.getFilename());}@Overridepublic void visit(VideoFile video) {System.out.println("Playing video file: " + video.getFilename());}
}// 編碼操作訪問者
class EncodeVisitor implements MediaFileVisitor {@Overridepublic void visit(AudioFile audio) {System.out.println("Encoding audio file: " + audio.getFilename());}@Overridepublic void visit(VideoFile video) {System.out.println("Encoding video file: " + video.getFilename());}
}public class VisitorDemo {public static void main(String[] args) {MediaFile audio = new AudioFile("song.mp3");MediaFile video = new VideoFile("movie.mp4");MediaFileVisitor playVisitor = new PlayVisitor();MediaFileVisitor encodeVisitor = new EncodeVisitor();audio.accept(playVisitor);video.accept(playVisitor);audio.accept(encodeVisitor);video.accept(encodeVisitor);}
}

在這個例子中,MediaFile 接口定義了接受訪問者的方法。AudioFileVideoFile 是具體的媒體文件類。MediaFileVisitor 接口定義了訪問者的行為,而 PlayVisitorEncodeVisitor 是具體的訪問者實現,它們實現了對不同媒體文件進行播放和編碼的操作。這樣,當需要為媒體文件添加新的操作時,我們只需要添加新的訪問者,而不必修改媒體文件類。

主要符合的設計原則
  • 開閉原則(Open-Closed Principle)
    • 訪問者模式允許在不修改現有代碼的情況下引入新的操作。這意味著類可以保持開放以供擴展(通過添加新的訪問者來實現新的功能),但對修改是關閉的(因為你不需要改變現有的類和對象結構)。
  • 單一職責原則(Single Responsibility Principle)
    • 在訪問者模式中,元素類的職責是維護其核心功能和數據,而訪問者類的職責是執行在這些元素上的特定操作。這種分離確保了單一職責原則,即每個類或模塊只有一個原因導致改變。
  • 依賴倒置原則(Dependency Inversion Principle)
    • 訪問者模式通常定義了抽象訪問者和抽象元素接口,具體的訪問者和元素類都依賴于這些接口,而不是具體的實現。這符合依賴倒置原則,即高層模塊不應該依賴于低層模塊的具體實現,而應該依賴于抽象。
  • 里氏替換原則(Liskov Substitution Principle)
    • 在訪問者模式中,可以用子類的對象替換父類的對象,而程序的行為不會發生變化。比如在訪問者模式中,可以用具體元素的子類來替換父類元素,而訪問者的行為不會改變。
在JDK中的應用
  • Java文件I/O(NIO)
    • java.nio.file.FileVisitor 接口是訪問者模式的一個經典應用。它用于遍歷文件系統的目錄樹。FileVisitor 接口定義了在訪問目錄樹的過程中可以執行的一系列操作(如訪問文件前后的操作),而具體的行為則由實現了 FileVisitor 接口的類定義。
    • SimpleFileVisitor 類是 FileVisitor 的一個實現,它提供了對遍歷過程中各種事件的基本處理。
  • Java編譯API
    • javax.lang.model 包中,Java編譯API使用了訪問者模式來處理抽象語法樹(AST)。這個API允許開發者在編譯時檢查、處理和生成Java代碼。
    • ElementElementVisitor 接口及其相關類在這個包中用于表示和訪問AST中的元素。
  • Java反射API
    • java.lang.reflect 包中,反射API提供了一種訪問者風格的接口,用于檢查類和對象的運行時行為。例如,Visitor 模式用于在不同類型的 AnnotatedElement(如類、方法、字段等)上執行操作。
在Spring中的應用
  • Spring框架中沒有直接的訪問者模式實例,但是框架的設計允許并鼓勵使用訪問者模式來實現跨多個類的操作和維護。

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

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

相關文章

【Python 訓練營】N_5 斐波那契數列

題目 輸出斐波那契數列 分析 斐波那契數列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又稱黃金分割數列&#xff0c;指的是這樣一個數列&#xff1a;0、1、1、2、3、5、8、13、21、34、……。 在數學上&#xff0c;費波那契數列是以遞歸的方法來定義&#xff…

9.9 Windows驅動開發:內核遠程線程實現DLL注入

在筆者上一篇文章《內核RIP劫持實現DLL注入》介紹了通過劫持RIP指針控制程序執行流實現插入DLL的目的&#xff0c;本章將繼續探索全新的注入方式&#xff0c;通過NtCreateThreadEx這個內核函數實現注入DLL的目的&#xff0c;需要注意的是該函數在微軟系統中未被導出使用時需要首…

用XMind2TestCase,測試更輕松

&#x1f4e2;專注于分享軟件測試干貨內容&#xff0c;歡迎點贊 &#x1f44d; 收藏 ?留言 &#x1f4dd; 如有錯誤敬請指正&#xff01;&#x1f4e2;交流討論&#xff1a;歡迎加入我們一起學習&#xff01;&#x1f4e2;資源分享&#xff1a;耗時200小時精選的「軟件測試」資…

C++ Qt QByteArray用法介紹

作者:令狐掌門 技術交流QQ群:675120140 csdn博客:https://mingshiqiang.blog.csdn.net/ 文章目錄 一、QByteArray的基本用法1、初始化和賦值2、訪問和修改元素3、 常用方法4、數據轉換二、QByteArray與文件操作三、QByteArray與網絡編程四、QByteArray數據編碼1、Base64 編解…

數據庫-MySQL之數據庫必知必會10-13章

第10章 創建計算字段 拼接字段 使用Concat()函數 執行算術計算 示例&#xff1a;從 Products 表中返回 prod_id、prod_price 和 sale_price。sale_price 是一個包含促銷價格的計算字段。提示&#xff1a;可以乘以 0.9&#xff0c;得到原價的 90%&#xff08;即 10%的折扣&…

2023.11.24 海豚調度,postgres庫使用

目錄 海豚調度架構dolphinscheduler DAG(Directed Acyclic Graph)&#xff0c; 個人自用啟動服務 DS的架構(海豚調度) 海豚調度架構dolphinscheduler 注:需要先開啟zookeeper服務,才能進行以下操作 通過UI進行工作流的配置操作, 配置完成后, 將其提交執行, 此時執行請求會被…

數組基礎知識

數組基礎&#xff08;不定時更新&#xff09; 數組基礎 數組基礎 &#xff08;1&#xff09;數組是存放在連續內存空間上的相同類型數據的集合。數組可以方便的通過下標索引的方式獲取到下標下對應的數據。數組下標都是從0開始的。數組內存空間的地址是連續的。 &#xff08;…

【科普知識】什么是步進電機?

德國百格拉公司于1973年發明了五相混合式步進電機及其驅動器&#xff0c;1993年又推出了性能更加優越的三相混合式步進電機。我國在80年代以前&#xff0c;一直是反應式步進電機占統治地位&#xff0c;混合式步進電機是80年代后期才開始發展。 步進電機是一種用電脈沖信號進行…

Verilog基礎:時序調度中的競爭(一)

相關閱讀 Verilog基礎https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 作為一個硬件描述語言&#xff0c;Verilog HDL常常需要使用語句描述并行執行的電路&#xff0c;但其實在仿真器的底層&#xff0c;這些并行執行的語句是有先后順序…

機器學習數據集整理:圖像、表格

前言 如果你對這篇文章感興趣&#xff0c;可以點擊「【訪客必讀 - 指引頁】一文囊括主頁內所有高質量博客」&#xff0c;查看完整博客分類與對應鏈接。 表格數據 Sklearn 提供了 13 個表格型數據&#xff0c;且數據處理接口統一&#xff1b;LIBSVM 提供了 131 個表格型數據&a…

【TypeScript】常見數據結構與算法(二):鏈表

文章目錄 鏈表結構&#xff08;LinkedList&#xff09;鏈表以及數組的缺點數組鏈表的優勢 什么是鏈表?封裝鏈表相關方法源碼鏈表常見面試題237-刪除鏈表中的節點206 - 反轉鏈表 數組和鏈表的復雜度對比 鏈表結構&#xff08;LinkedList&#xff09; 鏈表以及數組的缺點 鏈表…

AcWing103.電影——離散化

題目 莫斯科正在舉辦一個大型國際會議&#xff0c;有 n n n 個來自不同國家的科學家參會。 每個科學家都只懂得一種語言。 為了方便起見&#xff0c;我們把世界上的所有語言用 1 到 109 之間的整數編號。 在會議結束后&#xff0c;所有的科學家決定一起去看場電影放松一下。…

Interactive Visual Data Analysis

Words&Contents Home | Interactive Visual Data Analysis Book Outline 這本書對視覺、互動和分析方法進行了系統而全面的概述&#xff0c;作為數據可視化方面比較好的讀物&#xff1b; 目錄 Words&Contents Book Outline &#xff08;一&#xff09;Introduct…

AIGC 3D即將爆發,混合顯示成為產業數字化的生產力平臺

2023年&#xff0c;大語言模型與生成式AI浪潮席卷全球&#xff0c;以文字和2D圖像生成為代表的AIGC正在全面刷新產業數字化。而容易為市場所忽略的是&#xff0c;3D圖像生成正在成為下一個AIGC風口&#xff0c;AIGC 3D宇宙即將爆發。所謂AIGC 3D宇宙&#xff0c;即由文本生成3D…

VBA_MF系列技術資料1-227

MF系列VBA技術資料 為了讓廣大學員在VBA編程中有切實可行的思路及有效的提高自己的編程技巧&#xff0c;我參考大量的資料&#xff0c;并結合自己的經驗總結了這份MF系列VBA技術綜合資料&#xff0c;而且開放源碼&#xff08;MF04除外&#xff09;&#xff0c;其中MF01-04屬于定…

安裝compiler version 5

這個compiler version5 在我的資源里面可以免費下載&#xff1b; 另外這個東西還需要安裝&#xff0c;安裝教程在這里&#xff1a;Keil最新版保姆教程&#xff08;解決缺少V5編譯器問題&#xff09; - 嗶哩嗶哩 (bilibili.com) 看吧安裝好了year

C語言鏈表使用

目錄 雙鏈表增刪改查鏈表帶功能函數 雙鏈表增刪改查 #include <stdio.h> #include <stdlib.h>// 雙鏈表結點的定義 typedef struct DNode{int data;struct DNode *prev;struct DNode *next; } DNode;// 創建雙鏈表 DNode *createDoublyLinkedList() {int n, i;pri…

【C語言】qsort的秘密

一&#xff0c;本文目標 qsort函數可以對任意類型數據甚至是結構體內部的數據按照你想要的規則排序&#xff0c;它的功能很強大&#xff0c;可是為什么呢&#xff1f; 我將通過模擬實現qsort函數來讓你對這整個過程有一個清晰的深刻的理解。 二&#xff0c;qsort函數原型 v…

leetcode刷題詳解一

算法題常用API std::accumulate 函數原型&#xff1a; template< class InputIt, class T > T accumulate( InputIt first, InputIt last, T init );一般求和的&#xff0c;代碼如下&#xff1a; int sum accumulate(vec.begin() , vec.end() , 0);詳細用法參考 lo…

【python海洋專題四十七】風速的風羽圖

【python海洋專題四十七】風速的風羽圖 圖片 往期推薦 圖片 【python海洋專題一】查看數據nc文件的屬性并輸出屬性到txt文件 【python海洋專題二】讀取水深nc文件并水深地形圖 【python海洋專題三】圖像修飾之畫布和坐標軸 【Python海洋專題四】之水深地圖圖像修飾 【Pyth…