訪問者設計模式

訪問者設計模式是一種行為模式,允許您向現有對象結構添加新作,而無需修改其類。
它通過允許您將算法與其作的對象分開來實現這一點。
它在以下情況下特別有用:
您有一個復雜的對象結構(如 AST、文檔或 UI 元素),您希望對其執行多個不相關的作。
您希望 在不更改其源代碼的情況下向類添加新行為。
您需要根據對象的具體類型執行不同的作,而不是訴諸長鏈 或 檢查。if-elseinstanceof

通過訪問者模式, 您可以將作外部化為單獨訪問者客類。每個訪問者都會為每種元素類型實現行為,而元素只是接受訪問者。這可以保持數據結構的整潔,邏輯模塊化和可擴展性。

讓我們通過一個真實世界的示例來了解如何應用訪問者模式來將行為與結構清晰地分離,并使我們的系統更容易擴展,而無需觸及現有類。

問題:向形狀層次結構添加作
想象一下,您正在構建一個 支持多種形狀類型的矢量圖形編輯器:

Circle
Rectangle
Triangle

每個形狀都是公共層次結構的一部分,必須支持各種作,例如:

在屏幕上渲染
計算面積
導出為 SVG
序列化為 JSON

最簡單的方法是將所有這些方法添加到每個形狀類:

public?interface?Shape?{void?accept(ShapeVisitor?visitor);
}
public?class?Circle?implements?Shape?{private?final?double?radius;public?Circle(double?radius)?{this.radius?=?radius;}public?double?getRadius()?{return?radius;}@Overridepublic?void?accept(ShapeVisitor?visitor)?{visitor.visitCircle(this);}
}

為什么這會崩潰
此解決方案對于幾個作似乎很好,但隨著添加新的作或形狀類型,很快就會出現問題。

  1. 1. 違反單一責任原則
    每個形狀類現在包含多個不相關的職責:
    幾何計算
    繪圖
    序列化
    導出格式
    可能的印刷、造型等。
    這會使班級膨脹并使其更難維護。
  2. 2. 難以擴展
    如果您需要添加新作(例如 ),則必須:generatePdf()
    修改層次結構中的每個類
    重新編譯所有內容
    可能會破壞現有邏輯
    這違反了開放/封閉原則——類應該開放以進行擴展,但關閉以進行修改。
  3. 3. 你并不總是控制班級
    如果形狀類是第三方庫或生成的代碼的一部分,該怎么辦?您無法輕松直接添加新行為。
    我們真正需要什么
    我們需要一個解決方案,讓我們能夠:
    將作與形狀類分開
    添加新行為而不修改現有類
    避免重復檢查或使用類型開關來處理不同的形狀instanceof
    這正是訪問者設計模式旨在解決的問題。

訪問者模式
通過訪問者設計模式, 您可以將算法與其作的對象分開。它使您能夠將新作添加到類層次結構中,而無需修改類本身。

當您有以下情況時,這尤其有用:
一組穩定的元素類(例如形狀)
需要 跨這些類工作的一組作(例如,渲染、導出、計算)

類圖

?

  1. 1. 元素接口(例如Shape)
    表示對象結構中的對象(例如圖形形狀、文檔節點、AST 元素)。

聲明單個方法:
void accept(Visitor visitor);
每個想要訪問的類都必須實現此接口。
這允許訪問者被“接受”到對象中,以便它可以對其執行作。

  1. 2. 具體元素(例如, CircleRectangle)
    實現 接口。Element
    在方法中 ,他們使用 調用訪問者的相應方法 。accept()visitor.visitX(this)
  2. 3. 訪問者界面
    聲明一組 方法 — 每個具體元素類型一個。visit()
    每種方法都是為處理特定類型的元素而定制的。
    此接口允許您定義應用于模型中各種元素的外部作。
  3. 4. 混凝土訪問者(例如AreaCalculatorVisitor)
    實現 接口。Visitor
    每個訪問者都代表需要 跨元素執行的特定作。
    實現應用于元素的特定行為(例如,導出、驗證、轉換)

實現訪問者模式
讓我們使用 Visitor Pattern 重構具有多個形狀 (, ) 的圖形系統來執行兩個作:CircleRectangle

計算 每個形狀的面積
將形狀導出為 SVG 格式

  1. 1. 定義 接口(元素)Shape
    所有形狀都必須接受訪問者。
public?interface?Shape?{void?accept(ShapeVisitor?visitor);
}
  1. 2. 創建具體形狀類(元素)
    每個形狀類實現 并委托給訪問者。accept()

🔵 圈

public?class?Circle?implements?Shape?{private?final?double?radius;public?Circle(double?radius)?{this.radius?=?radius;}public?double?getRadius()?{return?radius;}@Overridepublic?void?accept(ShapeVisitor?visitor)?{visitor.visitCircle(this);}
}

🟥 矩形

public?class?Rectangle?implements?Shape?{private?final?double?width;private?final?double?height;public?Rectangle(double?width,?double?height)?{this.width?=?width;this.height?=?height;}public?double?getWidth()?{return?width;}public?double?getHeight()?{return?height;}@Overridepublic?void?accept(ShapeVisitor?visitor)?{visitor.visitRectangle(this);}
}
  1. 3. 定義訪問者界面
    每種方法對應于一種形狀類型。
public?interface?ShapeVisitor?{void?visitCircle(Circle?circle);void?visitRectangle(Rectangle?rectangle);
}
  1. 4. 實施具體訪問者
    📏 面積計算器訪問者
public?class?AreaCalculatorVisitor?implements?ShapeVisitor?{@Overridepublic?void?visitCircle(Circle?circle)?{double?area?=?Math.PI?*?circle.getRadius()?*?circle.getRadius();System.out.println("Area?of?Circle:?"?+?area);}@Overridepublic?void?visitRectangle(Rectangle?rectangle)?{double?area?=?rectangle.getWidth()?*?rectangle.getHeight();System.out.println("Area?of?Rectangle:?"?+?area);}
}

🖼? SVG 導出器訪問者

public?class?SvgExporterVisitor?implements?ShapeVisitor?{@Overridepublic?void?visitCircle(Circle?circle)?{System.out.println("<circle?r=\""?+?circle.getRadius()?+?"\"?/>");}@Overridepublic?void?visitRectangle(Rectangle?rectangle)?{System.out.println("<rect?width=\""?+?rectangle.getWidth()?+"\"?height=\""?+?rectangle.getHeight()?+?"\"?/>");}
}
  1. 5. 客戶端代碼
    現在,您可以使用任何訪問器對形狀結構進行作。
public?class?VisitorPatternDemo?{public?static?void?main(String[]?args)?{List<Shape>?shapes?=?List.of(new?Circle(5),new?Rectangle(10,?4),new?Circle(2.5));System.out.println("===?Calculating?Areas?===");ShapeVisitor?areaCalculator?=?new?AreaCalculatorVisitor();for?(Shape?shape?:?shapes)?{shape.accept(areaCalculator);}System.out.println("\n===?Exporting?to?SVG?===");ShapeVisitor?svgExporter?=?new?SvgExporterVisitor();for?(Shape?shape?:?shapes)?{shape.accept(svgExporter);}}
}

輸出

===?Calculating?Areas?===
Area?of?Circle:?78.53981633974483
Area?of?Rectangle:?40.0
Area?of?Circle:?19.634954084936208===?Exporting?to?SVG?===
<circle?r="5.0"?/>
<rect?width="10.0"?height="4.0"?/>
<circle?r="2.5"?/>

我們取得了什么成就
解耦邏輯:形狀類是干凈的;邏輯存在于訪問者中
開放/關閉原則:輕松添加新訪問者(例如,無需接觸形狀)JsonExporterVisitor
雙重調度:無需進行類型檢查或進行類型檢查instanceof
可重用性和可維護性:每個訪問者專注于一項作,并且可以單獨測試

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

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

相關文章

Linux_用 `ps` 按進程名過濾線程,以及用 `pkill` 按進程名安全殺進程

用 ps 按進程名過濾線程&#xff0c;以及用 pkill 按進程名安全殺進程摘要&#xff1a; 過濾線程信息&#xff1a;教你用 ps -C、pgrepps 等多種姿勢&#xff0c;既精準又避免誤殺。按名字殺進程&#xff1a;用 pkill 一把梭&#xff0c;優雅還是強殺隨你選&#xff0c;附帶“先…

關于國產 RAC 和分布式研討

本次研討核心目標是圍繞崖山 DB、達夢 DB、GBASE三款國產數據庫&#xff0c;以及數據庫內核開發呂工程師的分享&#xff0c;深入了解共享集群 RAC 的開發技術。但實際效果未達預期&#xff0c;參會者多圍繞 “共享集群與分布式應用場景” 泛泛而談&#xff0c;缺乏深度技術拆解…

傳輸層協議介紹

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔文章目錄前言一、TCP協議介紹二、TCP報文格式三、TCP三次握手四、TCP四次揮手五、UDP協議介紹六、常見協議及其端口七、TCP與UDP的不同總結前言提示&#xff1a;這里可以添加本…

Vibe Coding 概念提出者 AndrejKarpathy 談強化學習。

在預訓練時代&#xff0c;關鍵在于互聯網文本。你最需要的是一大批量、多樣化且高質量的互聯網文檔&#xff0c;供模型從中學習。在監督微調&#xff08;SFT&#xff09;時代&#xff0c;核心則是對話數據。人們雇傭合同工人為問題撰寫答案&#xff0c;類似于你在 Stack Overfl…

OSI模型和TCP/IP模型區別是什么

問題OSI模型和TCP/IP模型區別是什么我的回答OSI和TCP/IP這兩個協議棧有幾個主要區別&#xff1a;首先&#xff0c;層次結構不同。OSI是七層模型&#xff1a;物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。而TCP/IP是四層模型&#xff1a;數據鏈路層、網絡層、傳…

ros2與gazebo harmonic機械臂仿真項目Moveit2YoloObb的優化

文章目錄 關于項目RVIZ控制Gazebo Harmonic仿真機械臂GraphExecuter創建流程并通過Yolo算法抓取螺栓 關于項目 本文介紹ros2與gazebo harmonic機械臂仿真項目Moveit2YoloObb優化的內容&#xff0c;具體的代碼細節就不贅述了&#xff0c;主要還是演示效果&#xff0c;包括RVIZ控…

Linux 系統調優與CPU-IO-網絡內核參數調優

1. Linux系統調優1.1 安裝工具包在開始監控前&#xff0c;需要確保系統已安裝以下工具包&#xff0c;它們是后續操作的基礎&#xff1a;sysstat&#xff1a;包含 mpstat、iostat、sar 等核心統計工具iotop&#xff1a;專門監控磁盤 I/O 的進程級工具nethogs&#xff1a;按進程查…

laravel學習并連接mysql數據庫,給本地vue項目提供接口

下載laravel laravel下載地址phpstudy_pro\WWW\laravel.env文件 DB_CONNECTIONmysql DB_HOST127.0.0.1 DB_PORT3306 DB_DATABASEclgl //你的數據庫名稱 DB_USERNAMEroot //你的賬號 DB_PASSWORDroot //你的密碼安裝 Laravel CORS 包 composer require fruitcake/laravel-c…

Mybatis 與 Springboot 集成過程詳解

Mybatis 與 Springboot 集成過程詳解一. 核心概念與優勢二.Mybatis 核心類簡介1.MybatisAutoConfiguration2.MapperScans3.MapperScannerRegistrar4.MapperFactoryBean5.Configuration6.MapperRegistry7.MapperProxy 與 MapperProxyFactory7.1核心定位與職責7.22. ??MapperPr…

prometheus alertmanager 對接飛書

alertmanager 直接配置 飛書 的 webhook &#xff0c;發現并不滿足飛書接口的 json 格式。報錯如下levelerror ts2025-08-28T04:57:02.734Z callerdispatch.go:310 componentdispatcher msg"Notify for alerts failed" num_alerts23 err"prometheusalert-webhoo…

『專利好藥用力心腦血管健康』——愛上古中醫(28)(健康生活是coder抒寫優質代碼的前提條件——《黃帝內經》伴讀學習紀要)

心臟血管三通康&#xff0c;古時丸藥精益裝。 筆記模板由python腳本于2025-08-26 18:25:03創建&#xff0c;本篇筆記適合喜歡日常保健養生知識的coder翻閱。 學習的細節是歡悅的歷程 博客的核心價值&#xff1a;在于輸出思考與經驗&#xff0c;而不僅僅是知識的簡單復述。 Pyth…

在 .NET 8.0 中實現 JWT 刷新令牌

介紹在 Web 開發領域&#xff0c;安全是重中之重。JSON Web Tokens (JWT) 已成為在各方之間安全傳輸信息的熱門選擇。然而&#xff0c;在 JWT 過期后&#xff0c;如何維護用戶會話并避免頻繁登錄至關重要。這正是 JWT 刷新令牌應運而生的地方。在本文中&#xff0c;我們將指導您…

深入解析 git push 命令

1. 基礎語法 git push 的基本語法如下: git push <遠程倉庫名> <本地分支名>:<遠程分支名> [選項]<遠程倉庫名>: 通常是 origin(默認的遠程倉庫名稱)。 <本地分支名>:<遠程分支名>: 指定要推送的本地分支以及目標遠程分支。如果省略遠…

UI彈出動畫

簡介的UI彈出動畫 使用方式很簡單 掛載到需要彈出的目標 即可 using UnityEngine; using DG.Tweening; using Unity.VisualScripting;/// <summary>/// 簡潔的UI動畫腳本/// 直接掛載到UI組件上&#xff0c;調用Play()播放縮放彈出動畫/// </summary>public class …

PostgreSQL診斷系列(6/6):配置項全景解析——打造你的專屬優化清單

&#x1f517; 作為《PostgreSQL診斷系列》的收官之作&#xff0c;今天我們系統梳理 postgresql.conf 中的核心參數&#xff0c;將前5篇的“診斷”轉化為“調優”&#xff0c;打造一套生產環境專屬的配置模板。 你是否&#xff1a; 不知道哪些參數該調&#xff1f;害怕調錯導致…

Flink Slot 不足導致任務Pending修復方案

當前有3個虛擬機節點&#xff0c;每個節點配置的slot節點數量是4&#xff0c;${FLINK_HOME}/conf/flink-conf.yaml 關于slot的配置如下&#xff1a; # The number of task slots that each TaskManager offers. Each slot runs one parallel pipeline. taskmanager.numberOfTas…

亞馬遜合規風控升級:詳情頁排查與多賬號運營安全構建

2025年亞馬遜掀起的大規模掃號行動&#xff0c;聚焦商品詳情頁合規性審查&#xff0c;標志著跨境電商合規監管進入嚴風控時代&#xff0c;此次行動以關鍵詞規范與定價誠信為核心&#xff0c;大量賣家因內容違規遭遇賬號停用&#xff0c;對于賣家而言&#xff0c;構建系統化的合…

FISCO-BCOS-Python 模板

基于Python-SDK的FISCO BCOS區塊鏈HelloWorld模板&#xff0c;提供了簡單的問候語設置和查詢功能。本項目采用現代Python開發實踐&#xff0c;包含完整的配置管理、測試框架和項目結構。 快速開始 倉庫地址&#xff1a;git clone https://gitee.com/atanycosts/python-fisco-te…

移動端(微信等)使用 vConsole調試console

本文介紹了一種在移動端真機上進行調試的方法——使用VConsole。通過簡單的安裝步驟和代碼配置&#xff0c;開發者可以在移動端直接查看console.log輸出&#xff0c;極大提升了調試效率。 摘要生成于 C知道 &#xff0c;由 DeepSeek-R1 滿血版支持&#xff0c; 前往體驗 >作…

云計算資源分配問題

這里寫目錄標題一、云計算資源的基本類型二、資源分配的目標三、資源分配的方式四、資源分配的技術與工具五、挑戰與優化方向六、實際應用場景舉例總結云計算資源分配是指在云計算環境中&#xff0c;根據用戶需求、應用程序性能要求以及系統整體效率&#xff0c;將計算、存儲、…