23种设计模式-访问者(Visitor)设计模式
文章目录
- 一.什么是访问者模式?
- 二.访问者模式的结构
- 三.访问者模式的应用场景
- 四.访问者模式的优缺点
- 五.访问者模式的C++实现
- 六.访问者模式的JAVA实现
- 七.代码解释
- 八.总结
类图: 访问者设计模式类图
一.什么是访问者模式?
访问者模式(Visitor Pattern)是一种行为型设计模式,允许在不更改元素类的情况下,为对象结构中的元素增加新的操作。访问者模式通过将操作从元素类中抽离出来,实现操作的扩展。
在访问者模式中,核心思想是将数据结构和操作分开,数据结构负责提供必要的接口,而具体的操作逻辑由访问者实现。
二.访问者模式的结构
- Visitor(访问者接口):声明一组访问方法,每个方法对应一种具体的元素类型。
- ConcreteVisitor(具体访问者):实现访问者接口中声明的操作,对不同的元素提供不同的处理。
- Element(元素接口):定义一个Accept方法,用于接收访问者对象。
- ConcreteElement(具体元素):实现元素接口,并在Accept方法中调用访问者的相应方法。
- ObjectStructure(对象结构):提供元素的容器,负责存储和遍历元素。

三.访问者模式的应用场景
- 数据结构稳定,操作逻辑需要频繁变化:例如编译器中的语法树遍历。
- 需要对一组不相关的类执行操作:如不同形状的图形对象(圆形、矩形等)。
- 需要集中管理行为,而不想污染元素类。
四.访问者模式的优缺点
- 优点:
- 增加新的操作很方便,只需新增一个具体访问者类。
- 遵循单一职责原则,将操作与元素本身解耦。
- 缺点:
- 增加新的元素类型需要修改所有访问者,违背了开闭原则。
- 对象结构不容易扩展。
五.访问者模式的C++实现
#include <iostream>
#include <vector>
#include <memory>// 前置声明,避免循环引用
class ConcreteElementA;
class ConcreteElementB;
class Visitor;// 访问者接口(Visitor)
class Visitor {
public:virtual void visitConcreteElementA(ConcreteElementA& element) = 0;virtual void visitConcreteElementB(ConcreteElementB& element) = 0;virtual ~Visitor() = default;
};// 元素接口(Element)
class Element {
public:virtual void accept(std::shared_ptr<Visitor> visitor) = 0;virtual ~Element() = default;
};// 具体元素 A(ConcreteElementA)
class ConcreteElementA : public Element {
public:void accept(std::shared_ptr<Visitor> visitor) override {visitor->visitConcreteElementA(*this);}void operationA() {std::cout << "ConcreteElementA specific operation.\n";}
};// 具体元素 B(ConcreteElementB)
class ConcreteElementB : public Element {
public:void accept(std::shared_ptr<Visitor> visitor) override {visitor->visitConcreteElementB(*this);}void operationB() {std::cout << "ConcreteElementB specific operation.\n";}
};// 具体访问者 1(ConcreteVisitor1)
class ConcreteVisitor1 : public Visitor {
public:void visitConcreteElementA(ConcreteElementA& element) override {std::cout << "ConcreteVisitor1 visiting ConcreteElementA.\n";element.operationA();}void visitConcreteElementB(ConcreteElementB& element) override {std::cout << "ConcreteVisitor1 visiting ConcreteElementB.\n";element.operationB();}
};// 具体访问者 2(ConcreteVisitor2)
class ConcreteVisitor2 : public Visitor {
public:void visitConcreteElementA(ConcreteElementA& element) override {std::cout << "ConcreteVisitor2 visiting ConcreteElementA.\n";element.operationA();}void visitConcreteElementB(ConcreteElementB& element) override {std::cout << "ConcreteVisitor2 visiting ConcreteElementB.\n";element.operationB();}
};// 对象结构(ObjectStructure)
class ObjectStructure {
private:std::vector<std::shared_ptr<Element>> elements;public:void addElement(std::shared_ptr<Element> element) {elements.push_back(element);}void accept(std::shared_ptr<Visitor> visitor) {for (auto& element : elements) {element->accept(visitor);}}
};// 测试代码
int main() {// 创建对象结构ObjectStructure objectStructure;// 添加元素objectStructure.addElement(std::make_shared<ConcreteElementA>());objectStructure.addElement(std::make_shared<ConcreteElementB>());// 创建访问者 1 并访问std::shared_ptr<Visitor> visitor1 = std::make_shared<ConcreteVisitor1>();std::cout << "Using ConcreteVisitor1:\n";objectStructure.accept(visitor1);// 创建访问者 2 并访问std::shared_ptr<Visitor> visitor2 = std::make_shared<ConcreteVisitor2>();std::cout << "\nUsing ConcreteVisitor2:\n";objectStructure.accept(visitor2);return 0;
}
六.访问者模式的JAVA实现
// 抽象访问者
interface Visitor {void VisitConcreteElementA(ConcreteElementA elementA);void VisitConcreteElementB(ConcreteElementB elementB);
}// 具体访问者1
class ConcreteVisitor1 implements Visitor {@Overridepublic void VisitConcreteElementA(ConcreteElementA elementA) {System.out.println("ConcreteVisitor1 visiting ConcreteElementA");}@Overridepublic void VisitConcreteElementB(ConcreteElementB elementB) {System.out.println("ConcreteVisitor1 visiting ConcreteElementB");}
}// 具体访问者2
class ConcreteVisitor2 implements Visitor {@Overridepublic void VisitConcreteElementA(ConcreteElementA elementA) {System.out.println("ConcreteVisitor2 visiting ConcreteElementA");}@Overridepublic void VisitConcreteElementB(ConcreteElementB elementB) {System.out.println("ConcreteVisitor2 visiting ConcreteElementB");}
}// 抽象元素
abstract class Element {public abstract void Accept(Visitor visitor);
}// 具体元素A
class ConcreteElementA extends Element {@Overridepublic void Accept(Visitor visitor) {visitor.VisitConcreteElementA(this);}public void OperationA() {System.out.println("ConcreteElementA specific operation.");}
}// 具体元素B
class ConcreteElementB extends Element {@Overridepublic void Accept(Visitor visitor) {visitor.VisitConcreteElementB(this);}public void OperationB() {System.out.println("ConcreteElementB specific operation.");}
}// 对象结构
class ObjectStructure {private final List<Element> elements = new ArrayList<>();public void Add(Element element) {elements.add(element);}public void Accept(Visitor visitor) {for (Element element : elements) {element.Accept(visitor);}}
}// 客户端代码
public class VisitorPatternDemo {public static void main(String[] args) {// 创建对象结构ObjectStructure structure = new ObjectStructure();// 添加具体元素structure.Add(new ConcreteElementA());structure.Add(new ConcreteElementB());// 创建并使用访问者1ConcreteVisitor1 visitor1 = new ConcreteVisitor1();structure.Accept(visitor1);// 创建并使用访问者2ConcreteVisitor2 visitor2 = new ConcreteVisitor2();structure.Accept(visitor2);}
}
七.代码解释
- Element 接口和 ConcreteElement:
- Element接口定义了一个accept方法,具体元素类需要实现该方法,并在其中调用访问者的相应方法。
- Visitor 接口和 ConcreteVisitor:
- Visitor接口声明了针对每种具体元素的访问方法。
- ConcreteVisitor1和ConcreteVisitor2实现了Visitor接口,并定义了对ConcreteElementA和ConcreteElementB的具体操作。
- ObjectStructure 类:
- ObjectStructure维护了一个元素集合,并提供accept方法,用于将访问者传递给集合中的每个元素。
- 测试代码:
- 创建了一个对象结构,并添加了两种具体元素。
- 使用两个不同的访问者访问元素集合,展示了访问者模式的扩展性。
八.总结
访问者模式是一种将操作和数据结构分离的强大工具。当需要频繁添加新的操作或行为时,它提供了扩展性良好的解决方案。通过访问者模式,可以大幅简化复杂操作的管理,提升代码的可读性和维护性。
相关文章:
23种设计模式-访问者(Visitor)设计模式
文章目录 一.什么是访问者模式?二.访问者模式的结构三.访问者模式的应用场景四.访问者模式的优缺点五.访问者模式的C实现六.访问者模式的JAVA实现七.代码解释八.总结 类图: 访问者设计模式类图 一.什么是访问者模式? 访问者模式(…...
ssm150旅游网站的设计与实现+jsp(论文+源码)_kaic
毕 业 设 计(论 文) 题目:旅游网站设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本旅游网站就是在这样的大…...
【SKFramework框架】一、框架介绍
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享QQ群:398291828小红书小破站 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 【Unity3D框架】SKFramework框架完全教程《全…...
Arcgis地图实战三:自定义导航功能的实现
文章目录 1.最终效果预览2.计算两点之间的距离3.将点线画到地图上4.动态展示点线的变化5.动态画线6.动态画点 1.最终效果预览 2.计算两点之间的距离 let dis this.utilsTools.returnDisByCoorTrans(qdXYData, zdXYData, "4549")当距离小于我们在配置文件中预设置的…...
LLaMA-Factory 上手即用教程
LLaMA-Factory 是一个高效的大型语言模型微调工具,支持多种模型和训练方法,包括预训练、监督微调、强化学习等,同时提供量化技术和实验监控,旨在提高训练速度和模型性能。 官方开源地址:https://github.com/hiyouga/L…...
黑马点评 秒杀下单出现的问题:服务器异常---java.lang.NullPointerException: null(已解决)
前言: 在此之前找了好多资料,查了很多,都没有找到对应解决的方法,虽然知道是userid为空,但不知道要修改哪里,还是自己的debug能力不足,以后得多加练习。。。 问题如下: 点击限时抢…...
购物街项目TabBar的封装
1.TabBar介绍 在购物街项目中 不论页面如何滚动 始终存在一个TabBar固定在该项目的底部 他在该项目中 扮演者选项卡栏的角色 内部存在若干选项 而选项中 固定存在两部分(图片文本) 其中主要涉及到TabBar/TabBarItem这些和业务无关的共享组件(建议存放于components/common中)、…...
C++游戏开发面试题及参考答案
目录 在游戏开发中,为什么选择 C++ 作为编程语言? 为什么 C++ 语言更适合游戏开发? 描述游戏中的碰撞检测的基本原理。 解释游戏中的碰撞检测机制,并用 C++ 举例说明如何实现。 描述游戏中的物理模拟的基本原理。 阐述游戏中的物理模拟,如重力模拟在 C++ 中的实现方…...
字符串的基本操作(C语言版)
一、实验内容: 采用顺序结构存储串,编写一个函数substring(strl,str2),用于判定str2是否为strl的子串;编写一个函数,实现在两个已知字符串中找出所有非空最长公共子串的长度和最长公共子串的个数; ①字符…...
C缺陷与陷阱 — 7 可移植性缺陷
目录 1 应对C语言标准变更 2 标识符的名称限制 3 整数的大小 4 字符是有符号整数还是无符号整数 5 移位运算符 6 内存位置0 7 除法运算时发生的截断 1 应对C语言标准变更 使用新特性可以使代码更容易编写且减少错误,但可能会导致代码在旧编译器上无法编译。…...
应急响应:玄机_Linux后门应急
https://xj.edisec.net/challenges/95 11关做出拿到万能密码,ATMB6666,后面都在root权限下操作 1、主机后门用户名称:提交格式如:flag{backdoor} cat /etc/passwd,发现后门用户 flag{backdoor} 2、主机排查项中可以…...
C++:捕获 shared_from_this()和捕获this的区别
两种方法的主要区别在于对象的生命周期管理以及捕获方式的不同。以下是对两种方法的详细对比: 第一种:捕获 shared_from_this() 的方法 event.subscribe([self shared_from_this()]() {std::cout << "Event triggered, object is alive.&qu…...
网络协议之TCP
一、定义 TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。TCP旨在适应支持多网络应用的分层协议层次结构。在因特网协议族(Internet p…...
《澳鹏AI全景报告2024》分析最新的数据挑战
华盛顿州柯克兰市,2024 年 10 月 22 日 —— Appen Limited(澳大利亚证券交易所代码:APX),一家为人工智能生命周期提供高质量数据的领先供应商,发布了其《2024 年人工智能现状报告》。该报告对美国多个行业…...
【Java每日面试题】—— String、StringBuilder和StringBuffer的区别?
1、String 不可变性:String对象创建后不可变,内容不能被修改,对字符串修改会产生一个新的字符串对象。 线程:线程安全 适用:字符串内容不发生变化或少量字符串操作 String str = "Hello"; str = str + " World"; 2、StringBuffer 不可变性:对…...
【设计模式】【创建型模式(Creational Patterns)】之单例模式
单例模式是一种常用的创建型设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。 单例模式的原理 单例模式的核心在于控制类的实例化过程,通常通过以下方式实现: 私有化构造函数,防止外部直接实例化。…...
form表单的使用
模板 <template><el-form :model"formData" ref"form1Ref" :rules"rules"><el-form-item label"手机号" prop"tel"><el-input v-model"formData.tel" /></el-form-item><el-f…...
PDF内容提取,MinerU使用
准备环境 # python 3.10 python3 -m pip install huggingface_hub python3 -m pip install modelscope python3 -m pip install -U magic-pdf[full] --extra-index-url https://wheels.myhloli.com下载需要的模型 import json import osimport requests from huggingface_hub…...
SpringCloud篇(服务网关 - GateWay)
目录 一、简介 二、为什么需要网关 二、gateway快速入门 1. 创建gateway服务,引入依赖 2. 编写启动类 3. 编写基础配置和路由规则 4. 重启测试 5. 网关路由的流程图 6. 总结 三、断言工厂 四、过滤器工厂 1. 路由过滤器的种类 2. 请求头过滤器 3. 默认…...
自动化测试之unittest框架详解
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 unittest 1、什么是Unittest框架? python自带一种单元测试框架 2、为什么使用UnitTest框架? >批量执行用例 >提供丰富的断…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
