行为型设计模式—访问者模式

访问者模式:将数据结构对象与数据操作分离的设计模式,可以在不改变数据结构对象类结构的前提下定义作用于这些对象的新的操作。

访问模式结构:

  • 访客接口 (Visitor) 声明了一系列以表示对象结构的具体元素为参数的访问者方法。 如果编程语言支持重载, 这些方法的名称可以是相同的, 但是其参数一定是不同的。
  • 具体访客 (Concrete Visitor) 会为不同的具体元素类实现相同行为的几个不同版本。
  • 元素 (Element) 接口,声明了一个方法来 “接收” 访问者。 该方法必须有一个参数被声明为访问者接口类型。
  • 具体元素 (Concrete Element) 必须实现接收方法。 该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。 请注意, 即使元素基类实现了该方法, 所有子类都必须对其进行重写并调用访客对象中的合适方法。

有点像迭代器模式的结构,可以把迭代器看成访问者模式的特例,迭代器是吧遍历操作与具体数据结构解耦,而访问者模式把任何操作与一类对象解耦。

访问者模式通过将算法与对象结构分离来工作,这里说的算法指的是对对象的操作。为此,需要定义了一个表示算法的接口–Visitor。该接口将为对象结构中的每个类(一般称为元素类)提供一个方法。每个方法都将元素类的一个实例作为参数。表示对象结构的所有元素类也会实现一个Element接口,该接口定义了接受访问者的方法Accpet。此方法将访问者接口的实现作为参数。当Accpet方法被调用时,访问者实例对应的方法就会被调用,通过访问者完成对元素类实例的操作。

访问者模式允许你在结构体中添加行为, 而又不会对结构体造成实际变更。 假设代码库中包含不同的形状结构体, 如:

  • 方形
  • 圆形
  • 三角形

上述每个形状结构体都实现了通用形状接口。

首先定义一个如下访问者接口:

type visitor interface {visitForSquare(square)visitForCircle(circle)visitForTriangle(triangle)
}

可以使用 visit­For­Square­(square)visit­For­Circle­(circle)以及 visit­For­Triangle­(triangle)函数来为方形、 圆形以及三角形添加相应的功能。

accept接受方法添加至形状接口中。如果添加任何其他行为, 比如 get­Num­Sides获取边数和 get­Middle­Coordinates获取中点坐标 , 可使用相同的 accept­(v visitor)函数, 而无需对形状结构体进行进一步的修改。

func accept(v visitor)

定义对象接口

type Shape interface {getType() stringaccept(Visitor)
}

实现接口

type Square struct {side int
}func (s *Square) accept(v Visitor) {v.visitForSquare(s)
}func (s *Square) getType() string {return "Square"
}
type Circle struct {radius int
}func (c *Circle) accept(v Visitor) {v.visitForCircle(c)
}func (c *Circle) getType() string {return "Circle"
}
type Rectangle struct {l intb int
}func (t *Rectangle) accept(v Visitor) {v.visitForrectangle(t)
}func (t *Rectangle) getType() string {return "rectangle"
}

实现不同的访问方法

type AreaCalculator struct {area int
}func (a *AreaCalculator) visitForSquare(s *Square) {// Calculate area for square.// Then assign in to the area instance variable.fmt.Println("Calculating area for square")
}func (a *AreaCalculator) visitForCircle(s *Circle) {fmt.Println("Calculating area for circle")
}
func (a *AreaCalculator) visitForrectangle(s *Rectangle) {fmt.Println("Calculating area for rectangle")
}
package mainimport "fmt"type MiddleCoordinates struct {x inty int
}func (a *MiddleCoordinates) visitForSquare(s *Square) {// Calculate middle point coordinates for square.// Then assign in to the x and y instance variable.fmt.Println("Calculating middle point coordinates for square")
}func (a *MiddleCoordinates) visitForCircle(c *Circle) {fmt.Println("Calculating middle point coordinates for circle")
}
func (a *MiddleCoordinates) visitForrectangle(t *Rectangle) {fmt.Println("Calculating middle point coordinates for rectangle")
}

执行代码

package mainimport "fmt"func main() {square := &Square{side: 2}circle := &Circle{radius: 3}rectangle := &Rectangle{l: 2, b: 3}areaCalculator := &AreaCalculator{}square.accept(areaCalculator)circle.accept(areaCalculator)rectangle.accept(areaCalculator)fmt.Println()middleCoordinates := &MiddleCoordinates{}square.accept(middleCoordinates)circle.accept(middleCoordinates)rectangle.accept(middleCoordinates)
}

执行结果

Calculating area for square
Calculating area for circle
Calculating area for rectangleCalculating middle point coordinates for square
Calculating middle point coordinates for circle
Calculating middle point coordinates for rectangle

访客模式有如下优点

  • 解耦了数据结构与数据操作,使得操作集合可以独立变化。
  • 可以通过扩展访问者角色,实现对数据集的不同操作,程序扩展性更好。
  • 元素具体类型并非单一,访问者均可操作。
  • 各角色职责分离,符合单一职责原则。

与此同时它也有以下缺点

  • 无法增加元素类型:若系统数据结构对象易于变化,经常有新的数据对象增加进来,则访问者类必须增加对应元素类型的操作,违背了开闭原则。
  • 具体元素变更困难:具体元素增加属性、删除属性等操作, 会导致对应的访问者类需要进行相应的修改,尤其当有大量访客类时,修改范围太大。
  • 违背依赖倒置原则:为了达到“区别对待”,访问者角色依赖的是具体元素类型,而不是抽象接口。

访问者模式与其他设计模式:

  • 将访问者模式视为 命令模式 的加强版本, 其对象可对不同类的多种对象执行操作。
  • 使用访问者对整个 组合模式树 执行操作。
  • 可以同时使用访问者和 迭代器模式 来遍历复杂数据结构, 并对其中的元素执行所需操作, 即使这些元素所属的类完全不同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/661427.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Paimon + StarRocks 极速批流一体湖仓分析

摘要:本文整理自阿里云智能高级开发工程师王日宇,在 Flink Forward Asia 2023 流式湖仓(二)专场的分享。本篇内容主要分为以下四部分: StarRocksPaimon 湖仓分析的发展历程使用 StarRocksPaimon 进行湖仓分析主要场景和…

ONLYOFFICE是一站式协作免费开源办公神器

一、前言 众所周知,我们工作都离不开办公软件,一个好的办公软件可以大幅度提高工作效率,就目前市场而言,常见的办公软件有微软的 Microsoft Office 以及国产的 WPS Office,微软的 Microsoft Office是付费的&#xff0…

在JAVA中如何删除ZIP等等的压缩文件

删除ZIP文件的步骤和要点 JAVA中文件的类型在JAVA中能够被删除的文件类型问题:既然如此,那为什么有时候会遇到文件无法被删除的问题?心得总结 JAVA中文件的类型 在Java中,只存在两种文件类型,一种是文件夹&#xff0c…

postgresql和kingbase关于模糊查询大小写兼容问题

在mysql中,会有相关的like关键词,并且默认的是忽略大小写的。但是在postgresql和kingbase中,只有ilike关键字,并且默认是大小写敏感的。当我们使用mybatisplus的时候,默认提供的api也只有like()。这里提供一种方式来对…

CANoe实际项目中文件夹的规划

本人,之前设计了一个CANoe工程,由于工程设计之初没有设计好文档的归纳分类,导致文件查找起来非常费劲。 为了避免以后出现文件混乱,不可查找的问题,故特此归纳说明。 建立工程时: 第1步就应该设计好文档…

基于51单片机的加油站计费系统

基于51单片机的加油站计费系统[proteus仿真] 计费检测系统这个题目算是课程设计和毕业设计中常见的题目了,本期是一个108基于51单片机的加油站计费系统 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】,赞赏任意文章 2¥&#…

“软件老兵”的新征程:从代码到方案,40岁转身更显智慧锋芒

在快速迭代的软件行业,许多经验丰富的软件工程师,随着年岁的增长,发现自己在纯技术领域的竞争力似乎逐渐不敌那些刚刚踏入职场的00后工程师。 面对这一挑战,一些“软件老兵”选择转换跑道,将目光投向了方案工程师这一新…

EasyExcel实现三级联动

前言 项目中需要在导出的模板中新增三级联动的功能,类似省市区的联动。在网上找了一些方法,都不能直接使用,需要进行修改。本文主要分享一下,改后的代码,可以直接使用。 代码 public class CascadeWriteHandler imp…

Linux同时安装多个版本的JDKlinux多版本JDK切换配置- [Centos 安装JDK]

Linux同时安装多个版本的JDK&linux多版本JDK切换配置- [Centos 安装JDK] 一、查询系统是否已经安装jdk rpm -qa|grep java 或 rpm -qa|grep jdk 或 rpm -qa|grep gcj 二、卸载已安装的jdk Centos7存在已安装的JDK1.7时,可以使用以下方式删除1.7的openJDK rp…

「优选算法刷题」:两整数之和(位运算解法)

一、题目 给你两个整数 a 和 b ,不使用 运算符 和 - ,计算并返回两整数之和。 示例 1: 输入:a 1, b 2 输出:3示例 2: 输入:a 2, b 3 输出:5二、思路解析 其实笔试遇到这种题&…

python数据类型-元组

1 元组(tuple)的定义 元组是集合类型,和列表类似,元组中的元组类型可以不同,元素间用逗号隔开,和列表的不同之处在于: 1 元组的元组不可改变,也被称为只读列表 2 且元组用括号()表示,列表用方括…

月入过万比打工强,在家就能做steam搬砖项目真的假的

每天都有粉丝私下跟我聊天,讨论Steam搬砖项目到底是不是真的,到底能不能做。你想让我详细说说。那么今天就和大家详细聊聊这个月入过万元的项目。 简单来说,Steam搬砖项目就是在国外蒸汽上采购游戏道具,在国内网易buff平台上销售…

C# 求幂算法,最大公约数,最小公倍数

求幂算法概念 求幂算法是一种用于计算一个数的幂的算法。在C#中,可以使用两种方法来实现求幂操作:使用Math.Pow()函数或使用循环实现乘法运算。 方式1 double result Math.Pow(baseNumber, exponent); 方式2 double result 1; for (int i 0; i &l…

CVPR 2023: DBARF: Deep Bundle-Adjusting Generalizable Neural Radiance Fields

我们使用以下 6 个分类标准来解释本文的研究主题: 1. 神经表示: 隐式: 这种表示使用神经网络直接将空间中的 3D 点映射到其颜色和密度。网络充当“黑盒”函数,其内部工作原理无法直接解释。示例包括 NeRF、MPR-NeRF 和 Plenoxels。显式: 这种表示将每个体素或顶点的颜色和密…

实践理解 Transactional 是否生效

实践理解 Transactional 是否生效 示例: 新建一个订单表 tb_order_test 。 手动插入一条数据。 然后在代码中,根据 id 更新,如果更新成功,那么 update_time 会变化。 代码中执行 1/0; 由于 0不能做为除数,代码会抛…

MySQL 可重复读隔离级别,完全解决幻读了吗?

文章目录 前言一、什么是幻读?二、快照读是如何避免幻读的?三、当前读是如何避免幻读的?四、幻读被完全解决了吗?场景1场景2 总结 前言 MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读…

第 10 章:在C++中使用协程进行系统编程

最后一章专门介绍了一项对系统编程非常有用的功能,但这在C标准中相对较新。协程对象迅速找到了应用,成为了一等公民的状态机对象。它们的强大之处在于隐藏了协程帧后面的逻辑。请注意,这是一个高级主题,且C的协程接口既不简单也不…

交叉注意力融合2024创新方案汇总,附配套模块和代码

多模态学习和注意力机制是当前深度学习研究的热点领域之一,而交叉注意力融合作为这两个领域的结合点,具有很大的发展空间和创新机会。 作为多模态融合的一个重要组成部分,交叉注意力融合通过注意力机制在不同模块之间建立联系,促…

【C语言】main函数的参数形式

一、思考 在C语言中int main(),是最初接触该语言使用的形式。后面学习到函数部分,意识到int main()就是int main(void),意为main函数没有参数。再到后面却又有int main(int argc, char *argv[])这种参数形式。 不禁疑问,main函数的…

校招春招,在线测评一般测试哪些内容?

在校园招聘这一块,很多应届毕业生会相当在乎,对于他们来说,如果在学校期间就找到工作是比较轻松的事情,不用担心毕业之后找工作困难重重,可以稳稳当当毕业。但想要迅速通过招聘也不容易,在校招春招上面&…