C++ 设计模式 - 访问者模式

news/2025/2/11 11:09:26/

一:概述

        访问者模式将作用于对象层次结构的操作封装为一个对象,并使其能够在不修改对象层次结构的情况下定义新的操作。

        《设计模式:可复用面向对象软件的基础》一书中的访问者模式因两个原因而具有传奇色彩:一是因为它的复杂性,二是因为它使用了一种名为“双重分派”的技术。双重分派指的是根据对象和函数参数选择成员函数的过程。当然,访问者模式的复杂性主要在于 C++ 本身不支持双重分派。在讨论单分派和双重分派之前,让我先谈谈访问者模式

       什么是“双重分派”?双重分派(Double Dispatch)是一个动态选择调用方法的机制,其中被调用的具体函数依赖于两个对象的运行时类型,而不仅仅是一个对象的类型。这在多态编程中尤其有用,能够实现复杂的行为决策。在 C++ 中,双重分派通常通过虚函数和函数重载的组合实现,例如在访问者模式中。

二:使用场景

        1. 操作应该在对象层次结构上执行。

         2. 操作变更频繁。

         3. 对象层次结构是稳定的。 

三:类结构设计

        

  • Visitor 类:在对象结构上定义访问操作(visit 方法),用来描述对象结构中各元素可能执行的行为。

  • ConcreteVisitor 类:实现 Visitor 接口中的具体访问操作,为不同的元素提供特定的处理逻辑。

  • Element 类:表示对象结构中的元素,定义了 accept 方法,用于接收 Visitor 的操作请求。

  • ConcreteElement 类:具体的对象结构元素,实现 accept 方法,将自己作为参数传递给访问者,以完成双向的操作绑定。

四:一个具体的例子

        

      访问者模式有两种类型的层次结构:对象层次结构(CarElement)和操作层次结构(CarElementVisitor)。对象层次结构相对稳定,但操作层次结构可能需要支持新操作。CarElement 和 CarElementVisitor 都充当接口,这意味着每个具体的汽车元素(如 Wheel、Engine、Body 和 Car)都必须实现 accept(CarElementVisitor) 成员函数。相应地,每个具体的操作(如 CarElementDoVisitor 和 CarElementPrintVisitor)都必须实现四个重载的 visit(Wheel)visit(Engine)visit(Body)visit(Car) 方法。

      假设操作 CarElementPrintVisitor 被应用于对象层次结构。CarElementPrintVisitor 的任务可能是打印被访问的汽车零部件的名称。首先,像 Engine 这样的汽车元素接受访问者(accept(CarElementVisitor)),并使用访问者通过 visitor.visit(this) 调用操作层次结构,将自身作为参数传递。这确保了调用 CarElementPrintVisitor 上的 visit(Engine) 重载。访问 Car 是特殊的,因为 Car 由多个汽车元素组成。因此,Caraccept 成员函数将接受调用委托给它的所有汽车零部件。

     关于访问者的一个关键特征是:它依赖于两个对象,决定执行什么操作:访问者和被访问的对象。

五:代码示例

#include <iostream>
#include <string>
#include <vector>// 前向声明 Visitor 和元素类
class CarElementVisitor;class CarElement {
public:// 接受访问者操作的接口,由具体元素实现virtual void accept(CarElementVisitor& visitor) const = 0;virtual ~CarElement() = default;
};// 前向声明具体元素类
class Body;
class Car;
class Engine;
class Wheel;// 访问者接口,定义了针对不同元素的访问操作
class CarElementVisitor {
public:// 针对不同具体元素的访问操作的虚函数virtual void visit(Body body) const = 0;virtual void visit(Car car) const = 0;virtual void visit(Engine engine) const = 0;virtual void visit(Wheel wheel) const = 0;virtual ~CarElementVisitor() = default;
};// 轮胎类,表示汽车的一个轮胎
class Wheel : public CarElement {
public:// 构造函数,接收轮胎的名称Wheel(const std::string& n) : name(n) { }// 接受访问者,调用访问者的 visit(Wheel) 方法void accept(CarElementVisitor& visitor) const override {visitor.visit(*this);}// 获取轮胎的名称std::string getName() const {return name;}
private:std::string name; // 轮胎名称
};// 车身类,表示汽车的车身
class Body : public CarElement {
public:// 接受访问者,调用访问者的 visit(Body) 方法void accept(CarElementVisitor& visitor) const override {visitor.visit(*this);}
};// 发动机类,表示汽车的发动机
class Engine : public CarElement {
public:// 接受访问者,调用访问者的 visit(Engine) 方法void accept(CarElementVisitor& visitor) const override {visitor.visit(*this);}
};// 汽车类,表示一辆汽车,由多个汽车元素组成
class Car : public CarElement {
public:// 构造函数,接收一个汽车元素列表Car(std::initializer_list<CarElement*> carElements) : elements{ carElements } {}// 接受访问者,依次让每个汽车元素接受访问void accept(CarElementVisitor& visitor) const override {for (auto elem : elements) {elem->accept(visitor); // 委托访问操作给子元素}visitor.visit(*this); // 最后访问汽车本身}
private:std::vector<CarElement*> elements; // 汽车元素列表
};// 执行动作的访问者类,定义具体的访问行为
class CarElementDoVisitor : public CarE

六:相关设计模式 

  • 稳定的对象层次结构通常会应用 组合模式(Composite Pattern)
  • 迭代器模式(Iterator Pattern) 通常用于遍历对象层次结构。
  • 新的元素可以通过创建型模式(例如 工厂方法(Factory Method)原型模式(Prototype Pattern))来创建

七:优缺点

优点:

  • 可以轻松地向操作层次结构中添加新的操作(访问者)。
  • 一个操作可以被封装在一个访问者中。
  • 在遍历对象层次结构时,可以构建和维护状态。

缺点:

  • 如果需要在对象层次结构中添加新的被访问对象(VisitedObject),修改会非常困难。
  • 需要将新的被访问对象(VisitedObject)添加或从对象层次结构中移除。
  • 需要扩展访问者的接口,并在每个具体访问者中添加或移除 visit(VisitedObject) 成员函数。


八:参考:

1. The Visitor Pattern – MC++ BLOG
2. https://commons.wikimedia.org/w/index.php?curid=122709059


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

相关文章

【时时三省】(C语言基础)基础习题1

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 1.什么是程序&#xff1f;什么是程序设计 程序是为实现特定目标或解决特定问题&#xff0c;用计算机能理解和执行的语言编写的一系列指令的集合。 程序设计是问题分析&#xff0c;设计算法…

STM32的HAL库开发---高级定时器PWM输入模式实验

一、PWM输入模式工作原理 通过PWM输入模式&#xff0c;可以测量PWM的周期、频率、占空比。 时钟源选择内部时钟源&#xff0c;然后设置PSC预分频系数。F1系列内部时钟为72M的&#xff0c;PSC分频系数设置为0&#xff0c;也就是不分频。可以计算出计数器计一个数的时间&#xf…

《量化绿皮书》Chapter 3 Calculus and Linear Algebra 微积分与线性代数(一)

《A Practical Guide To Quantitative Finance Interviews》&#xff0c;被称为量化绿皮书&#xff0c;是经典的量化求职刷题书籍之一&#xff0c;包含以下七章&#xff1a; Chapter 1 General Principles 通用技巧 Chapter 2 Brain Teasers 脑筋急转弯 Chapter 3 Calculus and…

ubuntu安装VMware报错/dev/vmmon加载失败

ubuntu安装VMware报错/dev/vmmon加载失败&#xff0c;解决步骤如下&#xff1a; step1&#xff1a;为vmmon和vmnet组件生成密钥对 openssl req -new -x509 -newkey rsa:2048 -keyout VMW.priv -outform DER -out VMW.der -nodes -days 36500 -subj "/CNVMware/"ste…

网络安全行业的冬天

冬天已经来了&#xff0c;春天还会远吗&#xff1f;2022年10月28日&#xff0c;各个安全大厂相继发布了财报&#xff0c;纵观2022年前三季度9个月&#xff0c;三六零亏了19亿&#xff0c;奇安信亏了11亿&#xff0c;深信服亏了6亿&#xff0c;天融信亏了4亿&#xff0c;安恒亏了…

JAVA程序员面试总结

第一阶段&#xff1a;三年 我认为三年对于程序员来说是第一个门槛&#xff0c;这个阶段将会淘汰掉一批不适合写代码的人。这一阶段&#xff0c;我们走出校园&#xff0c;迈入社会&#xff0c;成为一名程序员&#xff0c;正式从书本上的内容迈向真正的企业级开发。我们知道如何…

Kokoro 开源文本转语音引擎上线!多语言支持,无需联网,浏览器内极速运行

Kokoro 是一款轻量级的开源文本转语音(TTS)引擎,凭借其高效能和轻量化设计,迅速在技术社区中引起关注。本文将详细介绍 Kokoro 的主要特点,并提供在浏览器和 Python 环境中的代码示例,帮助您快速上手。 1. Kokoro:可在浏览器中运行的 TTS 引擎 1.1 简介 Kokoro 是一个…

gitlab多项目流水线

背景是我有多个项目&#xff0c;希望其中一个项目被触发的时候&#xff0c;联动另外一个项目自动打包。然后我就看文档尝试操作了一下&#xff0c;所以有本文。 官方文档参考&#xff1a;https://gitlab.cn/docs/14.5/jh/ci/pipelines/multi_project_pipelines.html 不知道是不…