当前位置: 首页 > news >正文

设计模式1-访问者模式

访问者模式是一种行为设计模式,它允许你定义在对象结构中的元素上进行操作的新操作,而无需修改这些元素的类。这种模式的主要思想是将算法元素的结构分离开,使得可以在不修改元素结构的情况下定义新的操作。

所谓算法与元素结构分离,即保持元素(被访问对象)结构的稳定,而将算法置于访问者之中,因为访问者可以新建,这样就符合了OCP(开闭原则)。

在访问者模式中,有两个主要的角色:

  1. 访问者(Visitor)
    定义了在对象结构中访问元素时的新操作接口。通常会有多个不同的访问者,每个访问者实现一组特定的操作。

  2. 元素(Element)
    定义了接受访问者的接口,通常会有多个不同的元素,每个元素实现了接口并提供了接受访问者的方法。

访问者模式的主要优势在于当需要在一组对象上执行一些复杂的操作时,你可以通过添加新的访问者而不是修改每个元素的类来扩展系统。

类图:

 

时序图:

 

下面是一个简单的 Java 示例,演示了访问者模式的基本结构:

// 访问者接口
interface Visitor {void visit(ElementA elementA);void visit(ElementB elementB);
}// 元素接口
interface Element {void accept(Visitor visitor);
}// 具体的元素A
class ElementA implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}// 元素A的特定操作void operationA() {System.out.println("Performing operation A on ElementA");}
}// 具体的元素B
class ElementB implements Element {@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}// 元素B的特定操作void operationB() {System.out.println("Performing operation B on ElementB");}
}// 具体的访问者
class ConcreteVisitor implements Visitor {@Overridepublic void visit(ElementA elementA) {elementA.operationA();}@Overridepublic void visit(ElementB elementB) {elementB.operationB();}
}// 客户端代码
public class VisitorPatternExample {public static void main(String[] args) {Element elementA = new ElementA();Element elementB = new ElementB();Visitor visitor = new ConcreteVisitor();elementA.accept(visitor); // 执行 ElementA 的操作elementB.accept(visitor); // 执行 ElementB 的操作}
}

在这个例子中,Visitor 接口定义了两个访问方法,每个方法对应一个具体的元素。Element 接口定义了接受访问者的方法。具体的元素类 ElementAElementB 实现了 Element 接口,并分别实现了自己的特定操作。ConcreteVisitor 是一个具体的访问者类,实现了对每个元素的访问操作。

在客户端代码中,我们创建了一个 ConcreteVisitor 实例,并让每个元素接受这个访问者。换言之,是在Client中操作了访问者与元素的连接印证了类图。这样,通过不同的访问者,我们可以执行不同的操作,而不需要修改元素的类。这是访问者模式的一种简单示例,实际中可能涉及更复杂的场景和多个元素。

怎么定义新的操作?

当使用访问者模式时,定义新的操作就是创建新的实现了访问者接口的具体访问者类。每个具体访问者类负责实现一组特定的操作,而这些操作可以是全新的、与原有操作不相关的,或者是对现有操作的扩展。

举个例子,假设我们有一个图形结构,包括圆形(Circle)和矩形(Rectangle)两种图形。现在我们希望实现两种不同的操作:计算图形的面积和计算图形的周长。

首先,定义图形接口和具体的图形类:

// 图形接口
interface Shape {void accept(Visitor visitor);
}// 圆形类
class Circle implements Shape {private double radius;public Circle(double radius) {this.radius = radius;}public double getRadius() {return radius;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}// 矩形类
class Rectangle implements Shape {private double width;private 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(Visitor visitor) {visitor.visit(this);}
}

然后,定义访问者接口和具体的访问者类:

// 访问者接口
interface Visitor {void visit(Circle circle);void visit(Rectangle rectangle);
}// 计算面积的具体访问者类
class AreaCalculator implements Visitor {@Overridepublic void visit(Circle circle) {double area = Math.PI * circle.getRadius() * circle.getRadius();System.out.println("Area of Circle: " + area);}@Overridepublic void visit(Rectangle rectangle) {double area = rectangle.getWidth() * rectangle.getHeight();System.out.println("Area of Rectangle: " + area);}
}// 计算周长的具体访问者类
class PerimeterCalculator implements Visitor {@Overridepublic void visit(Circle circle) {double perimeter = 2 * Math.PI * circle.getRadius();System.out.println("Perimeter of Circle: " + perimeter);}@Overridepublic void visit(Rectangle rectangle) {double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());System.out.println("Perimeter of Rectangle: " + perimeter);}
}

现在我们可以在不修改图形类的情况下定义新的操作。例如,创建了两个具体的访问者类 AreaCalculatorPerimeterCalculator 分别用于计算图形的面积和周长。在客户端代码中,我们可以通过接受不同的访问者来执行不同的操作:

public class VisitorExample {public static void main(String[] args) {Shape circle = new Circle(5);Shape rectangle = new Rectangle(4, 6);Visitor areaCalculator = new AreaCalculator();Visitor perimeterCalculator = new PerimeterCalculator();circle.accept(areaCalculator); // 计算圆形的面积circle.accept(perimeterCalculator); // 计算圆形的周长rectangle.accept(areaCalculator); // 计算矩形的面积rectangle.accept(perimeterCalculator); // 计算矩形的周长}
}

这样,通过定义不同的访问者,我们可以轻松地扩展系统,而无需修改图形类的结构。

结论:

访问者模式很好地实现了访问算法开放被访问元素封闭,有这种需求时就可以考虑使用访问者模式。

相关文章:

设计模式1-访问者模式

访问者模式是一种行为设计模式,它允许你定义在对象结构中的元素上进行操作的新操作,而无需修改这些元素的类。这种模式的主要思想是将算法与元素的结构分离开,使得可以在不修改元素结构的情况下定义新的操作。 所谓算法与元素结构分离&#x…...

Android meminfo 查看方法及解析

目录 Android 上查看memory 信息的方法 内存限制的信息 手动释放缓存 例 adb shell dumpsys meminfo pid 解析 adb shell dumpsys meminfo 汇总信息说明 Total RAM Free RAM ION Used RAM Lost RAM ZRAM /proc/meminfo 参考文档 Android 上查看memory 信息的方法 …...

微信小程序解决华为手机保存图片到相册失败

1.新增隐私设置 2.优化代码 新增uni.authorize判断 _saveCode() {let that this;console.log(点击了保存图片)console.log(this.result)uni.authorize({scope: scope.writePhotosAlbum,success(e) {console.log(e)if (this.result ! "") {uni.saveImageToPhotosAlb…...

板块零 IDEA编译器基础:第三节 下载和在IDEA中集成 Tomcat服务器 来自【汤米尼克的JAVAEE全套教程专栏】

板块零 IDEA编译器基础:第三节 下载和在IDEA中集成 Tomcat服务器 一、为什么选择Tomcat(1)常见的JAVA WEB服务器(2)选择Tomcat的理由 二、Tomcat 8.5下载解压三、Tomcat 结构目录四、在IDEA中集成Tomcat 假设我们已经…...

2024/2/6

一、填空题 1、一个类的头文件如下所示&#xff0c;num初始化值为5&#xff0c;程序产生对象T&#xff0c;且修改num为10&#xff0c;并使用show()函数输出num的值10。 #include <iostream.h> class Test { private: static int num; public: Test(int); void sho…...

mysql清空表数据后如何让自增ID仍从1开始

有2种方法&#xff1a; 1、清空表时使用truncate命令&#xff0c;而不用delete命令 truncate test; 使用truncate命令的好处&#xff1a; 1&#xff09;、速度快 2&#xff09;、可以对自增ID进行重排&#xff0c;使自增ID仍从1开始计算 2、清空表数据后&#xff0c;使用alter…...

C++集群聊天服务器 数据模块+业务模块+CMake构建项目 笔记 (上)

跟着施磊老师做C项目&#xff0c;施磊老师_腾讯课堂 (qq.com) 本文在此篇博客的基础上继续实现数据模块和业务模块代码&#xff1a; C集群聊天服务器 网络模块业务模块CMake构建项目 笔记 &#xff08;上&#xff09;-CSDN博客https://blog.csdn.net/weixin_41987016/article…...

#Js篇:字符串的使用方法es5和es6

字符串 \ &#xff1a;单引号&#xff08;\u0027&#xff09;\" &#xff1a;双引号&#xff08;\u0022&#xff09; charAt 定义&#xff1a; 返回指定位置的字符&#xff0c;参数时从0开始编号的位置 参数&#xff1a; 位置下标 abc.charAt(1) // "b" …...

SpringBoo+Vue构建简洁日志文件查看系统

点击下载《SpringBooVue构建日志文件查看系统&#xff08;源代码&#xff09;》 1. 前言 想必经常做java开发的小伙伴&#xff0c;其大多数服务都是运行在linux系统上的&#xff0c;当遇到一些比较棘手的bug需要处理时&#xff0c;经常要上服务器去捞日志&#xff0c;然后通过…...

JavaScript基础第二天

JavaScript基础第二天 今天我们学习if分支语句、三元表达式和switch-case语句。 1. if分支语句 1.1 语法 if (条件表达式){// 满足条件要执行的语句 } else {// 不满足条件要执行的语句 }if中的内容如果为true&#xff0c;就执行大括号的代码块&#xff0c;如果为false执行…...

2、卷积和ReLU激活函数

python了解集合网络如何创建具有卷积层的特性。 文章目录 简介特征提取(Feature Extraction)卷积过滤(Filter with Convolution)Weights(权重)激活(Activations)用ReLU检测示例 - 应用卷积和ReLU结论In [1]: import numpy as np from itertools import productdef show_kerne…...

SQL世界之基础命令语句

目录 一、SQL SELECT 语句 1.SQL SELECT 语法 2.SQL SELECT 实例 3.SQL SELECT * 实例 二、SQL SELECT DISTINCT 语句 1.语法 2.使用 DISTINCT 关键词 三、SQL SELECT WHERE 语句 1.WHERE 子句 2.语法 3.使用 WHERE 子句 4.引号的使用 四、SQL SELECT AND&OR …...

Facebook未来展望:社交媒体的下一个篇章

社交媒体一直是连接人与人之间的纽带&#xff0c;而Facebook则一直在推动这一领域的发展。随着科技不断演进和社会需求的不断变迁&#xff0c;Facebook正积极筹谋社交媒体的下一个篇章。本文将深入剖析Facebook的未来展望&#xff0c;探讨其在社交媒体领域所迎接的新时代。 1. …...

源码搭建教学:直播带货商城小程序开发

结合小程序开发的直播带货商城&#xff0c;不仅可以提供更便捷的购物体验&#xff0c;还可以实现更高效的销售。因此&#xff0c;学习如何搭建一个直播带货商城小程序将成为您拓展商业领域的利器。 步骤一&#xff1a;准备工作 在开始开发之前&#xff0c;您需要进行一些准备工…...

vue-cli引入本地json数据:封装为js文件,无需请求直接读取

vue-cli引入本地json数据 1、新建js文件&#xff08;路径自定义&#xff09;&#xff0c;写入JSON数据 /* jsonData.js */export let jsonData { // 声明变量&#xff0c;存储数据// JSON源数据 }2、组件内引入js文件&#xff0c;读取数据 /* Example.vue */import { json…...

20240202在Ubuntu20.04.6下使用whisper.cpp的显卡模式

20240202在Ubuntu20.04.6下使用whisper.cpp的显卡模式 2024/2/2 19:43 【结论&#xff1a;在Ubuntu20.04.6下&#xff0c;确认large模式识别7分钟中文视频&#xff0c;需要356447.78 ms&#xff0c;也就是356.5秒&#xff0c;需要大概5分钟&#xff01;效率太差&#xff01;】 …...

前端面试拼图-数据结构与算法

摘要&#xff1a;总结一些前端算法题&#xff0c;持续更新&#xff01; 一、数据结构与算法 时间复杂度-程序执行时需要的计算量&#xff08;CPU&#xff09; 空间复杂度-程序执行时需要的内存空间 前端开发&#xff1a;重时间&#xff0c;轻空间 1.把一个数组旋转k步 arr…...

在C++的union中使用std::string(非POD对象)的陷阱

struct和union的对比 union最开始是C语言中的关键字&#xff0c;在嵌入式中比较常见&#xff0c;由于嵌入式内存比较稀缺&#xff0c;所以常用union用来节约空间&#xff0c;在其他需要节省内存的地方也可以用到这个关键字&#xff0c;写一个简单程序来说明union的用途 struc…...

Spring Cloud Netflix Eureka的参数调优

下面主要分为Client端和Server端两大类进行简述&#xff0c;Eureka的几个核心参数 客户端参数 Client端的核心参数 参数默认值说明eureka.client.availability-zones告知Client有哪些region以及availability-zones&#xff0c;支持配置修改运行时生效eureka.client.filter-o…...

Wireshark不显示Thrift协议

使用Wireshark对thrift协议进行抓包&#xff0c;但是只显示了传输层的tcp协议&#xff1a; "右键" -> "Decode As" 选择thrift的tcp端口 将“当前”修改为Thrift&#xff0c;然后点击“确定” 设置后&#xff0c;可以发现Wireshark里面显示的协议从Tcp变…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...