访问者设计模式是一种行为模式,允许您向现有对象结构添加新作,而无需修改其类。
它通过允许您将算法与其作的对象分开来实现这一点。
它在以下情况下特别有用:
您有一个复杂的对象结构(如 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;将计算、存储、…