设计模式 22 访问者模式 Visitor Pattern

设计模式 22 访问者模式 Visitor Pattern

1.定义


访问者模式是一种行为型设计模式,它允许你在不改变已有类结构的情况下,为一组对象添加新的操作。它将算法与对象结构分离,使你能够在不修改现有类的情况下,为这些类添加新的操作。

2.内涵

访问者模式核心概念,有以下几点:

  1. 访问者 (Visitor): 定义了一个访问具体元素的方法,每个方法对应一个具体元素类。
  2. 具体访问者 (ConcreteVisitor): 实现访问者接口,定义对具体元素类的访问逻辑。
  3. 元素 (Element): 定义一个 accept 方法,接受访问者对象并调用访问者的 visit 方法。
  4. 具体元素 (ConcreteElement): 实现元素接口,提供访问者访问其内部状态的方法。

相关UML 图如下所示:

        

解释:

  • Visitor 接口:定义了 visit 方法,接受一个 Element 对象作为参数。
  • ConcreteVisitor 类:实现了 Visitor 接口,并定义了对 Element 对象的具体操作逻辑。
  • Element 接口:定义了 accept 方法,接受一个 Visitor 对象作为参数,并调用访问者的 visit 方法。
  • ConcreteElement 类:实现了 Element 接口,并提供 operation 方法,用于访问其内部状态。

3.案例分析
/*** The Visitor Interface declares a set of visiting methods that correspond to* component classes. The signature of a visiting method allows the visitor to* identify the exact class of the component that it's dealing with.*/
class ConcreteComponentA;
class ConcreteComponentB;class Visitor {public:virtual void VisitConcreteComponentA(const ConcreteComponentA *element) const = 0;virtual void VisitConcreteComponentB(const ConcreteComponentB *element) const = 0;
};/*** The Component interface declares an `accept` method that should take the base* visitor interface as an argument.*/class Component {public:virtual ~Component() {}virtual void Accept(Visitor *visitor) const = 0;
};/*** Each Concrete Component must implement the `Accept` method in such a way that* it calls the visitor's method corresponding to the component's class.*/
class ConcreteComponentA : public Component {/*** Note that we're calling `visitConcreteComponentA`, which matches the* current class name. This way we let the visitor know the class of the* component it works with.*/public:void Accept(Visitor *visitor) const override {visitor->VisitConcreteComponentA(this);}/*** Concrete Components may have special methods that don't exist in their base* class or interface. The Visitor is still able to use these methods since* it's aware of the component's concrete class.*/std::string ExclusiveMethodOfConcreteComponentA() const {return "A";}
};class ConcreteComponentB : public Component {/*** Same here: visitConcreteComponentB => ConcreteComponentB*/public:void Accept(Visitor *visitor) const override {visitor->VisitConcreteComponentB(this);}std::string SpecialMethodOfConcreteComponentB() const {return "B";}
};/*** Concrete Visitors implement several versions of the same algorithm, which can* work with all concrete component classes.** You can experience the biggest benefit of the Visitor pattern when using it* with a complex object structure, such as a Composite tree. In this case, it* might be helpful to store some intermediate state of the algorithm while* executing visitor's methods over various objects of the structure.*/
class ConcreteVisitor1 : public Visitor {public:void VisitConcreteComponentA(const ConcreteComponentA *element) const override {std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor1\n";}void VisitConcreteComponentB(const ConcreteComponentB *element) const override {std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor1\n";}
};class ConcreteVisitor2 : public Visitor {public:void VisitConcreteComponentA(const ConcreteComponentA *element) const override {std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor2\n";}void VisitConcreteComponentB(const ConcreteComponentB *element) const override {std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor2\n";}
};
/*** The client code can run visitor operations over any set of elements without* figuring out their concrete classes. The accept operation directs a call to* the appropriate operation in the visitor object.*/
void ClientCode(std::array<const Component *, 2> components, Visitor *visitor) {// ...for (const Component *comp : components) {comp->Accept(visitor);}// ...
}int main() {std::array<const Component *, 2> components = {new ConcreteComponentA, new ConcreteComponentB};std::cout << "The client code works with all visitors via the base Visitor interface:\n";ConcreteVisitor1 *visitor1 = new ConcreteVisitor1;ClientCode(components, visitor1);std::cout << "\n";std::cout << "It allows the same client code to work with different types of visitors:\n";ConcreteVisitor2 *visitor2 = new ConcreteVisitor2;ClientCode(components, visitor2);for (const Component *comp : components) {delete comp;}delete visitor1;delete visitor2;return 0;
}

输出:

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2

上述代码类之间关系 UML图,如下所示


4.注意事项

访问者模式在实施过程中,需要考虑以下几个方面,以确保代码的正确性、可维护性和可扩展性:

1. 元素接口的设计:

元素接口应该定义一个 accept 方法,用于接受访问者对象。
accept 方法应该调用访问者的 visit 方法,并将自身作为参数传递给 visit 方法。
元素接口的设计应该尽可能通用,以支持不同类型的元素。


2. 访问者接口的设计:

访问者接口应该定义一个或多个 visit 方法,每个方法对应一个具体元素类。
visit 方法应该接受对应元素类对象作为参数,并执行对该元素类的操作。
访问者接口的设计应该尽可能灵活,以支持不同的操作。


3. 具体访问者类的实现:

每个具体访问者类应该实现访问者接口,并定义对不同元素类的操作逻辑。
具体访问者类的实现应该尽可能清晰、简洁,以提高代码可读性和可维护性。


4. 访问者模式的应用场景:

访问者模式适用于需要为一组对象添加新的操作,而不想修改这些对象的类的情况。
访问者模式也适用于需要对一组对象进行不同的操作,而这些操作之间没有明显的关联的情况。
访问者模式还可以用于将算法逻辑与对象结构分离,提高代码的可读性和可维护性。


5. 访问者模式的局限性:

访问者模式可能会增加代码的复杂性,尤其是当需要处理多种类型的元素时。
访问者模式可能会破坏元素类的封装性,因为访问者需要访问元素类的内部状态。

5.最佳实践


访问者模式是一种强大的工具,但使用不当会导致代码复杂化或破坏封装性。以下是一些最佳实践,帮助你有效地使用访问者模式:

1. 限制访问者数量:

避免过度使用访问者,只在需要为一组对象添加新操作,且不希望修改这些对象本身时使用。
尽量保持访问者数量较少,否则会增加代码复杂度,难以维护。


2. 保持访问者职责单一:

每个访问者应该只负责一种操作,避免将多个操作混杂在一个访问者中。
职责单一的访问者更容易理解和维护,也更容易进行扩展。


3. 避免访问者修改元素状态:

访问者应该主要负责执行操作,而不是修改元素状态。
如果需要修改元素状态,应该通过元素本身的方法来进行,而不是通过访问者。


4. 使用泛型或模板:

对于需要处理多种类型元素的访问者,可以使用泛型或模板来简化代码。
泛型或模板可以避免重复代码,提高代码可读性和可维护性。


5. 考虑使用组合模式:

如果需要对多个层次结构的元素进行操作,可以考虑将访问者模式与组合模式结合使用。
组合模式可以帮助你构建树形结构,而访问者模式可以帮助你遍历树形结构并执行操作。


6. 谨慎使用访问者模式:

访问者模式并非万能的,在某些情况下,其他设计模式可能更适合。
仔细分析你的需求,选择最合适的模式来解决问题。


6.总结

        访问者模式是一种强大的设计模式,但需要谨慎使用。在实施访问者模式时,需要仔细考虑元素接口和访问者接口的设计,以及具体访问者类的实现。同时,需要了解访问者模式的局限性,并选择合适的应用场景。

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

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

相关文章

Flink系列一:flink光速入门 (^_^)

引入 spark和flink的区别&#xff1a;在上一个spark专栏中我们了解了spark对数据的处理方式&#xff0c;在 Spark 生态体系中&#xff0c;对于批处理和流处理采用了不同的技术框架&#xff0c;批处理由 Spark-core,SparkSQL 实现&#xff0c;流处理由 Spark Streaming 实现&am…

什么是深拷贝和浅拷贝?

浅拷贝 浅拷贝是指将一个对象复制到另一个变量中&#xff0c;但是复制的是对象的地址&#xff0c;而不是对对象本身进行复制。原始对象的引用和复制对象的引用时期上是共享同一个内存地址的。 所以我们修改了复制引用指向的对象中的属性或方法&#xff0c;原始引用指向的对象…

metersphere发送kafka消息

上传jar包 设置前置脚本 import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.ProducerRecord; import java.util.Properties;// Kafka 生产者配置 Properties props new Properties(); props.put("bootstrap.servers&qu…

JavaWeb_SpringBootWeb

先通过一个小练习简单了解以下SpringBootWeb。 小练习&#xff1a; 需求&#xff1a;使用SpringBoot开发一个Web应用&#xff0c;浏览器发起请求/hello后&#xff0c;给浏览器返回字符串"Hello World~"。 步骤&#xff1a; 1.创建SpringBoot项目&#xff0c;勾选We…

【电源专题】功率电感啸叫对策及案例

在文章:【电源专题】功率电感器啸叫原因及典型案例 中我们了解到了电感器啸叫的原因和一些典型电路中产生电感啸叫的案例。通过案例我们了解到很多时候啸叫来源是DC-DC转换器的功率电感器,所以如果我们要降低或消除啸叫,那有哪些对策呢? 避免流过人耳可听频率电流 首先我们…

Spring Boot 中使用 Spring Retry 重试:再也不怕代码“掉链子”了

引言&#xff1a;生活需要重试&#xff0c;代码也一样&#xff01; 想象一下&#xff0c;你正在网上支付&#xff0c;结果网络突然卡顿&#xff0c;支付失败。这时候你会怎么做&#xff1f;当然是再试一次&#xff01;生活中我们经常会遇到各种“失败”&#xff0c;但我们会选…

猜猜我是谁游戏

猜谜过程 在TabControl控件中&#xff0c;第一个tab中放了一个PictureBox&#xff0c;里面有一张黑色的图片。 玩家点击显示答案按钮&#xff0c;切换图片。 设计器 private void button1_Click(object sender, EventArgs e){this.pictureBox1.Image Image.FromFile(&qu…

Covalent的CQT质押迁移比率在以太坊上升至13%,超Moonbeam记录

Covalent Network&#xff08;CQT&#xff09;作为领先的结构化模块化数据基础设施层&#xff0c;目前其在以太坊上的 CQT 质押比率已超过之前在 Moonbeam 上达到的历史最高水平。自从将质押合约迁移到以太坊不到一个月的时间里&#xff0c;超过总供应量的 13% 的 CQT 代币已被…

总结 HTTPS 的加密流程

一、前言 http是为了解决http存在的问题而在http基础上加入了SSL/TSL&#xff0c;在HTTP/2中TCP三次握手后会进入SSL/TSL握手&#xff0c;当SSL/TSL建立链接后&#xff0c;才会进行报文的传输。 二、HTTPS的混合加密 我们先来认识密钥&#xff1a; 密钥是用于加密和解密数据…

【MySQL事务(下)(重点)】

文章目录 再次理解MySQL事务一、MVCC机制数据库并发的场景有三种&#xff1a;3个记录隐藏列字段undo日志——由mysql维护的一段内存空间再次理解隔离性和隔离级别 Read View 理论部分RR 和 RC 的本质区别 再次理解MySQL事务 1.每个事务都有自己的事务ID&#xff0c;根据事务的…

Recognition:基于HoG特征的最近邻分类器与SVM的人物检测器

实际运行结果&#xff1a; 上面的为最近邻分类器&#xff0c;其中红框表示最近邻搜索的预测结果。下方的为SVM&#xff1a;橙色框表示SVM的预测结果。其中&#xff0c;最红的框表示SVM预测的最高得分的预测结果。 使用经典图像处理的方法开发简单人物检测器&#xff0c;其大致…

P148--章节作业1

编辑 编辑 public class Main {public static void main(String args[]){double yxq100000;int cishu0;while(true) {if(yxq > 50000) {yxq yxq - yxq * 0.05;cishucishu1;}else if(yxq > 1000){yxq yxq - 1000;cishucishu1;}else{break;}}System.out.print(cishu);} …

【分支控制】(switch) 详解

switch分支结构 基本语法 switch (表达式){case 常量1: //当...语句块1;break; //跳出switchcase 常量2:语句块2;break;...case 常量n;语句块n;break;default:default语句块;break;}switch 关键字, 表示switch分支表达式, 对应一个值case 常量1: 当表达式的值等于常量1, 就执行…

详解Spring MVC

目录 1.什么是Spring Web MVC MVC定义 2.学习Spring MVC 建立连接 RequestMapping 注解介绍及使用 获取单个参数 获取多个参数 获取普通对象 获取JSON对象 获取基础URL参数 获取上传文件 获取Header 获取Cookie 获取Session 总结 1.什么是Spring Web MVC 官⽅对于…

转型先锋!G7易流的数字化到底有多牛?

在供应链全球一体化进程中&#xff0c;国内外局势的改变&#xff0c;使得物流行业运力供大于求趋势愈加明显&#xff0c;国内供应链参与者面对内外发展需求和激烈的市场竞争&#xff0c;需要打破同质化竞争的局面&#xff0c;提供具有特色的服务&#xff0c;形成专业、高效、灵…

深度学习500问——Chapter09:图像分割(3)

文章目录 9.8 PSPNet 9.9 DeepLab系列 9.9.1 DeepLabv1 9.9.2 DeepLabv2 9.9.3 DeeoLabv3 9.9.4 DeepLabv3 9.8 PSPNet 场景解析对于无限制的开放词汇和不同场景来说是具有挑战性的。本文使用文中的 pyramid pooling module 实现基于不同区域的上下文集成&#xff0c;提出了PS…

OrangePi AIpro初识及使用大模型GPT-Neo-1.3B测试

OrangePi AIpro介绍 1.1. 开发板简介 Orange Pi AI Pro 开发板是香橙派联合华为精心打造的高性能AI 开发板&#xff0c;其搭 载了昇腾AI 处理器&#xff0c;可提供8TOPS INT8 的计算能力&#xff0c;内存提供了8GB 和16GB 两种版本。可以实现图像、视频等多种数据分析与推理…

[xx点评完结]——白马点评完整代码+rabbitmq实现异步下单+资料,免费

项目所有功能已测&#xff0c;均可以跑通&#xff0c;Jmeter和RabbitMQ也都测了。 项目源码:dianpinghui: 仿黑马点评项目 资料: https://pan.baidu.com/s/1kTCn9PxgeIey90WgM4KRqA?pwdn66b 对佬有帮助可以给个star哈&#xff0c;感谢&#x1f339;&#x1f339;&#x1f3…

OpenHarmony 实战开发——内核IPC机制数据结构解析

一、前言 OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;是由开放原子开源基金会&#xff08;OpenAtom Foundation&#xff09;孵化及运营的开源项目&#xff0c;目标是面向全场景、全连接、全智能时代&#xff0c;基于开源的方式&#xff0c;搭建一个…