设计模式 23 访问者模式

news/2024/9/18 12:30:15/ 标签: 设计模式, 访问者模式

设计模式 23

  • 创建型模式(5):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
  • 结构型模式(7):适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式、代理模式
  • 行为型模式(11):责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式

文章目录

访问者模式(Visitor Pattern)

1 定义

访问者模式通过引入一个访问者接口,使得你可以在元素类中接受访问者,并让访问者决定对元素的具体操作。访问者模式的关键在于分离算法和数据结构,使得新的操作可以轻松地添加而不影响已有的数据结构。

2 结构

访问者模式包含以下角色:

  • 访问者接口(Visitor): 为每种元素类型定义一个访问方法。访问者接口通常提供一个Visit方法,针对不同的元素类有不同的实现。
  • 具体访问者(ConcreteVisitor): 实现访问者接口的具体操作,对元素执行具体的操作。
  • 元素接口(Element): 声明一个Accept方法,该方法接受访问者对象并调用访问者的Visit方法。
  • 具体元素(ConcreteElement): 实现元素接口,定义元素的具体行为,并在Accept方法中调用访问者的对应方法。
  • 对象结构(ObjectStructure): 通常是一个包含多个不同类型元素的集合,它可以遍历这些元素并让访问者访问它们。

UML 类图

+---------------------------+         +---------------------------+
|      Visitor              | <------ |     Element               |
+---------------------------+         +---------------------------+
| + VisitElementA():void    |         | + Accept(v:Visitor): void |
| + VisitElementB():void    |         +---------------------------+
+---------------------------+               ^^                                   ||                                   |
+---------------------------+         +---------------------------+
| ConcreteVisitor           |         | ConcreteElement           |
+---------------------------+         +---------------------------+
| + VisitElementA():void    |         | + Accept(v:Visitor): void |
| + VisitElementB():void    |         | + OperationA(): void      |
+---------------------------+         +---------------------------+

3 示例代码

假设我们要实现一个报表系统,系统中包含不同类型的员工(如工程师和经理),每种员工有不同的报表要求。我们可以使用访问者模式来实现报表的生成,使得报表的生成与员工类型的实现分离。

访问者接口

// 访问者接口
public interface IVisitor
{void Visit(Engineer engineer);void Visit(Manager manager);
}

具体访问者

// 具体访问者 - 报表生成器
public class ReportGenerator : IVisitor
{public void Visit(Engineer engineer){Console.WriteLine($"Generating report for Engineer: {engineer.Name}");}public void Visit(Manager manager){Console.WriteLine($"Generating report for Manager: {manager.Name} with {manager.SubordinatesCount} subordinates.");}
}

元素接口

// 元素接口
public interface IEmployee
{void Accept(IVisitor visitor);
}

具体元素类

// 具体元素 - 工程师
public class Engineer : IEmployee
{public string Name { get; private set; }public Engineer(string name){Name = name;}public void Accept(IVisitor visitor){visitor.Visit(this);}
}// 具体元素 - 经理
public class Manager : IEmployee
{public string Name { get; private set; }public int SubordinatesCount { get; private set; }public Manager(string name, int subordinatesCount){Name = name;SubordinatesCount = subordinatesCount;}public void Accept(IVisitor visitor){visitor.Visit(this);}
}

对象结构

// 对象结构 - 员工列表
public class EmployeeStructure
{private List<IEmployee> _employees = new List<IEmployee>();public void Attach(IEmployee employee){_employees.Add(employee);}public void Detach(IEmployee employee){_employees.Remove(employee);}public void Accept(IVisitor visitor){foreach (var employee in _employees){employee.Accept(visitor);}}
}

客户端代码

class Program
{static void Main(string[] args){// 创建员工结构EmployeeStructure employeeStructure = new EmployeeStructure();// 添加员工employeeStructure.Attach(new Engineer("John"));employeeStructure.Attach(new Manager("Alice", 5));// 创建报表生成器ReportGenerator reportGenerator = new ReportGenerator();// 生成报表employeeStructure.Accept(reportGenerator);}
}

运行结果

Generating report for Engineer: John
Generating report for Manager: Alice with 5 subordinates.

在这个例子中,IVisitor 定义了对不同员工类型(EngineerManager)的访问方法。ReportGenerator 是具体的访问者,实现了生成报表的逻辑。IEmployee 接口定义了 Accept 方法,EngineerManager 作为具体的元素,实现了接受访问者的逻辑。EmployeeStructure 作为对象结构,管理了所有的员工,并允许访问者访问这些员工。

4 特点

  • 优点:

    • 增加新的操作容易: 可以在不修改元素类的情况下,通过添加新的访问者类来增加新的操作。

    • 将操作与对象结构分离: 访问者模式将数据结构和操作分离,使得数据结构和操作各自独立,符合单一职责原则。

    • 扩展性好: 可以很容易地增加新的访问者来实现新的功能。

  • 缺点:

    • 增加元素类的复杂性: 每个元素类都必须实现接受访问者的方法,这可能会增加类的复杂性。

    • 违反开闭原则: 如果需要修改元素的结构或添加新的元素类型,则需要修改所有的访问者类,这与开闭原则相违背。

    • 双分派: 访问者模式要求进行双分派,即根据元素类型和访问者类型分别调用相应的方法,这可能会导致系统的复杂性增加。

5 适用场景

  • 对象结构稳定: 当对象结构相对稳定,但操作经常变化时,使用访问者模式可以有效地管理这些变化。
  • 需要对对象结构中的对象进行复杂操作: 访问者模式适用于对对象结构中的元素进行复杂操作,且这些操作可能会频繁变化的场景。
  • 对象结构中包含多个不相关的类: 当对象结构中包含多个不相关的类,需要对这些类执行某些操作时,访问者模式可以通过统一的访问者接口来处理这些操作。

6 总结

访问者模式通过将操作分离到独立的访问者对象中,使得在不修改元素类的情况下,可以增加新的操作。它适用于对象结构稳定但操作经常变化的场景。然而,由于需要对每个元素类增加接受访问者的方法,并且可能导致违反开闭原则,因此在使用时需要权衡利弊。在需要对复杂对象结构进行扩展和管理时,访问者模式是一种强大的设计模式


http://www.ppmy.cn/news/1525094.html

相关文章

Vue3+TS项目给el-button统一封装一个点击后转圈效果的钩子函数按钮防抖

前言 每个按钮都要单独定义一个loading变量&#xff0c;并且在接口请求前修改为true&#xff0c;接口响应后再修改为false&#xff0c;封装后这段重复的逻辑就可以统一管理不用每次都写一遍了。 效果 新建一个公共的src\common.ts import { ref } from "vue"expor…

【有啥问啥】探索扫地机器人中的 SLAM 算法:原理、实现与未来展望

探索扫地机器人中的 SLAM 算法&#xff1a;原理、实现与未来展望 随着智能家居的普及&#xff0c;扫地机器人逐渐成为日常生活中的常见家电。其自主导航能力使得它能够在复杂的家庭环境中高效完成清洁任务&#xff0c;而这背后的核心技术之一就是 SLAM&#xff08;Simultaneou…

git的快速合并fast-forward merge详解

文章目录 1. 什么是快进合并&#xff1f;2. 快进合并的前提条件3. 快进合并的工作原理3.1 示例场景&#xff1a;3.2 使用命令&#xff1a;3.3 快进合并的视觉效果&#xff1a; 4. 快进合并的优点5. 快进合并的缺点6. 快进合并 vs 非快进合并6.1 非快进合并&#xff1a;6.2 非快…

【Linux】ps -ef 与 ps aux 的区别及 “|” “grep” 的详解

前言&#xff1a;虽然 ps -ef 与 ps aux 命令都能查看进程运行情况&#xff0c;但两者之间还是有一些细致区别。 一、格式与输出 1、ps -ef/ps -Af&#xff1a; -e是显示所有进程&#xff0c;包括其他用户的进程。-A属于-e别名&#xff0c;功能相同。 -f &#xff1a;显示更…

Android TextView 学习备忘

1.android:gravity 与 android:layout_gravity&#xff1a; Android TextView文本位置_mob649e8166858d的技术博客_51CTO博客https://blog.51cto.com/u_16175509/8597723 2.设置字体样式&#xff1a; android:fontFamily"font/my_custom_font"android:textStyle&qu…

Android SystemUI组件(05)状态栏-系统状态图标显示管理

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注下方 SystemBars分析中状态栏中的部分-系统状态图标显示&管理 即可。 1 系统状态图标显…

SenseGlove机器臂遥操作控制:技术优势与高危作业安全保障

在追求高效与安全的工业时代&#xff0c;高危作业任务始终是行业发展的一大障碍。SenseGlove力反馈手套机器臂遥操作应用案例的出现&#xff0c;凭借其独特的技术优势&#xff0c;为解决这一难题提供了创新性解决方案。 一、技术优势 高精度的力反馈技术&#xff1a;SenseGlove…

CTF——简单的《WEB》

文章目录 一、WEB1、easysql2、baby_web3、baby_sql4、upload_easy5、easygame拓展1.1拓展1.2 6、ht_ssti7、包容乃大 一、WEB 1、easysql 题目描述&#xff1a; sql注入漏洞 1.常用的sql注入测试语句 2.sql注入bypass 解题思路 这边提示基本给的也很完整的&#xff0c;不…

基于中心点的目标检测方法CenterNet—CVPR2019

Anchor Free目标检测算法—CenterNet Objects as Points论文解析 Anchor Free和Anchor Base方法的区别在于是否在检测的过程中生成大量的先验框。CenterNet直接预测物体的中心点的位置坐标。 CenterNet本质上类似于一种关键点的识别。识别的是物体的中心点位置。 有了中心点之…

关于Python爬虫的基础知识

爬虫是一种自动获取网页内容的程序或工具。以下是一些关于爬虫的基础知识&#xff1a; 一、爬虫的工作原理 发送请求&#xff1a; 爬虫首先向目标网站发送 HTTP 请求&#xff0c;就像你在浏览器中输入网址并访问一样。请求中包含了一些信息&#xff0c;如请求方法&#xff08;…

Spring Boot集成Akka Stream快速入门Demo

1.什么是Akka Stream&#xff1f; Akka Streams是一个用于处理和传输元素序列的库。它建立在Akka Actors之上&#xff0c;使流的摄入和处理变得简单。由于它是建立在Akka Actors之上的&#xff0c;它为Akka现有的actor模型提供了一个更高层次的抽象。Akka流由3个主要部分组成-…

Linux平台屏幕|摄像头采集并实现RTMP推送两种技术方案探究

技术背景 随着国产化操作系统的推进&#xff0c;市场对国产化操作系统下的生态构建&#xff0c;需求越来越迫切&#xff0c;特别是音视频这块&#xff0c;今天我们讨论的是如何在linux平台实现屏幕|摄像头采集&#xff0c;并推送至RTMP服务。 我们知道&#xff0c;Linux平台&…

洛谷刷题之B2089 数组逆序重存放

数组逆序重存放 题目入口 题目描述 将一个数组中的值按逆序重新存放。例如&#xff0c;原来的顺序为 8 , 6 , 5 , 4 , 1 8,6,5,4,1 8,6,5,4,1。要求改为 1 , 4 , 5 , 6 , 8 1,4,5,6,8 1,4,5,6,8。 输入格式 输入为两行&#xff1a;第一行数组中元素的个数 n n n&#x…

比 GPT-4 便宜 187 倍的Mistral 7B (非广告)

Mistral 7B 是一种设计用来快速处理较长文本的人工智能模型。它采用了一些特别的技术来提高速度和效率&#xff0c;比如“分组查询注意力&#xff08;grouped-query attention&#xff09;”和“滑动窗口注意力&#xff08;sliding-window attention&#xff09;”。 这些技术…

UNI-APP 富文本编辑器,可以对图片、文字格式进行编辑和混排。

✍找了几篇文章对比了一下&#xff0c;大体都差不多各有各的说辞和见解,但是没有提供/style/editor-icon.css文件&#xff0c;找起来虽然说不算太麻烦&#xff0c;但是不够直接&#xff0c;又要花费时间去弄&#xff0c;虽然用的不是很多但是&#xff0c;我还是决定自己写一篇&…

第15-05章:获取运行时类的完整结构

我的后端学习大纲 我的Java学习大纲 6.1.第一组方法API: 1.API列表&#xff1a;java.lang.Class 类&#xff1a; 2.代码测试&#xff1a; public class ReflectionUtils{ puvblic static void main(String[] args){}// 第一组Testpublic void api_01{//上面截图的代码......…

VR 尺寸美学主观评价-解决方案-现场体验研讨会报名

棣拓科技VR创新解决方案助力尺寸美学所见即所得! 诚邀各位行业专家莅临指导交流 请扫描海报二维码踊跃报名&#xff0c;谢谢 中国上海 2024.10.25 亮点介绍 1、通过精湛渲染技术&#xff0c;最真实展现设计效果&#xff0c;并通过VR设备一比一比例进行展现。 2、设置相关设…

基于ACMEv2协议的免费证书申请

项目&#xff1a;https://github.com/cook-code-jazor/acmex 非开源&#xff0c;使用webui管理证书的申请&#xff0c;所有文件本地化存储&#xff0c;支持windows/linux/osx. 运行 很简单,直接运行命令 ./acmex --runas console首次运行没有配置文件&#xff0c;会要求你填…

组件通信——provide 和 inject 实现爷孙组件通信

provide 和 inject 实现爷孙组件通信 介绍 provide 和 inject 是 Vue.js 提供的一种在组件之间共享数据的机制&#xff0c;它允许在组件树中的任何地方注入依赖项。这对于跨越多个层级的组件间通信特别有用&#xff0c;因此无需手动将 prop 数据逐层传递下去。 provide&#…

使用Selenium WebDriver捕获网络请求

在进行Web自动化测试时,捕获网络请求是十分重要的。通过这种方式,我们可以了解到页面加载过程中发生的网络活动,这对于调试、性能分析以及确保应用程序按预期工作都非常有用。本文将详细介绍如何使用Selenium WebDriver和Python来实现捕获网络请求的功能。 前置要求 在开始…