C++软件设计模式之访问者模式

访问者模式(Visitor Pattern)是行为型设计模式之一,它的主要目的是将数据结构和作用于结构上的操作分离。通过访问者模式,可以在不改变数据结构的前提下,增加对数据的新操作。这种模式尤其适用于需要对一个对象结构中的对象进行多态性访问的场景。

目的和意图

  1. 目的

    • 将数据结构和操作分离。
    • 实现对对象结构中对象的操作而不改变对象本身。
    • 提供一个灵活的机制来定义新的操作。
  2. 意图

    • 访问者模式定义一个对元素(Element)进行操作的访问者类,让用户可以在不改变元素类的前提下,定义新的操作。
    • 元素自己并不知道操作的具体实现,而是将操作委托给访问者。

适用场合

  1. 对象结构相对稳定,但需要对元素进行多种操作

    • 当对象结构在设计时就已经确定,并且需要频繁地添加新的操作时,访问者模式特别有用。通过访问者模式,可以避免频繁修改元素类。
  2. 操作不依赖于元素的状态

    • 如果操作需要访问元素的内部状态,但不需要修改它们,访问者模式可以提供一个干净的方式来实现这些操作。
  3. 需要对一个对象结构中的对象进行多态性访问

    • 在访问者模式中,元素会提供一个接受访问者的接口,而访问者会根据元素的类型执行相应的操作。

C++示例

假设我们有一个图形编辑器,其中包含不同类型的图形对象,如矩形和圆形。我们希望对这些图形对象进行不同的操作,比如绘制和计算面积,而不需要修改图形类本身。

1. 定义元素接口

首先,定义图形元素的基类。

class Shape {
public:
    virtual void accept(Visitor* visitor) = 0;
    virtual ~Shape() {}
};

2. 定义具体元素

定义矩形和圆形类。

class Rectangle : public Shape {
public:
    void accept(Visitor* visitor) override {
        visitor->visitRectangle(this);
    }
    int width, height;
};

class Circle : public Shape {
public:
    void accept(Visitor* visitor) override {
        visitor->visitCircle(this);
    }
    int radius;
};

3. 定义访问者接口

定义访问者基类,其中包含访问不同元素的方法。

class Visitor {
public:
    virtual void visitRectangle(Rectangle* rectangle) = 0;
    virtual void visitCircle(Circle* circle) = 0;
    virtual ~Visitor() {}
};

4. 定义具体访问者

定义绘制访问者和面积计算访问者。

class DrawVisitor : public Visitor {
public:
    void visitRectangle(Rectangle* rectangle) override {
        cout << "Drawing Rectangle with width: " << rectangle->width << ", height: " << rectangle->height << endl;
    }
    void visitCircle(Circle* circle) override {
        cout << "Drawing Circle with radius: " << circle->radius << endl;
    }
};

class AreaCalculator : public Visitor {
public:
    void visitRectangle(Rectangle* rectangle) override {
        int area = rectangle->width * rectangle->height;
        cout << "Rectangle area: " << area << endl;
    }
    void visitCircle(Circle* circle) override {
        double area = 3.14 * circle->radius * circle->radius;
        cout << "Circle area: " << area << endl;
    }
};

5. 客户端代码

在客户端代码中,我们可以创建不同的访问者,并对图形对象进行操作。

#include <iostream>
#include <vector>

int main() {
    Rectangle rect{10, 20};
    Circle circ{5};

    DrawVisitor drawVisitor;
    AreaCalculator areaCalculator;

    rect.accept(&drawVisitor);
    circ.accept(&drawVisitor);

    rect.accept(&areaCalculator);
    circ.accept(&areaCalculator);

    return 0;
}

代码特征

  1. 双重分派

    • 访问者模式利用了双重分派的特性,即在运行时确定元素和访问者的具体类型,并执行相应的方法。
  2. 开放-封闭原则

    • 访问者模式遵循开放-封闭原则,即在不修改现有代码的情况下,可以扩展新的访问者来实现新的操作。
  3. 元素类不改变

    • 元素类只需要提供一个接受访问者的接口,而不需要知道具体的操作细节。
  4. 访问者类知道所有元素类

    • 访问者类需要知道所有可能的元素类,并为每种元素类提供对应的操作方法。

通过访问者模式,可以方便地对对象结构中的对象进行多态性访问,同时保持元素类的稳定性,从而实现更好的代码扩展性和维护性。

 

访问者模式(Visitor Pattern)通常与其他设计模式协同使用,以增强其功能或解决特定的问题。以下是一些常见的与访问者模式协同使用的模式:

1. 组合模式(Composite Pattern)

共同使用场景

  • 当需要对一个包含复杂层次结构的对象集合(如树形结构)进行操作时,组合模式和访问者模式常常一起使用。

协同原因

  • 组合模式用于构建一个包含简单对象和复杂对象的树形结构。
  • 访问者模式可以用来遍历组合模式的树形结构,并对每个元素执行不同的操作。

示例

  • 在图形编辑器中,组合模式可以用来构建一个包含不同形状的图形层次结构。访问者模式可以用来对这些形状进行如绘制、计算面积等操作。

2. 迭代器模式(Iterator Pattern)

共同使用场景

  • 当需要遍历一个对象集合并对每个元素执行操作时,迭代器模式和访问者模式可以协同工作。

协同原因

  • 迭代器模式用于提供一种统一的方式来遍历集合中的元素。
  • 访问者模式用于定义对元素的操作。

示例

  • 在文件系统中,迭代器模式可以用来遍历文件和文件夹的集合。访问者模式可以用来对文件和文件夹执行不同的操作,如复制、删除等。

3. 策略模式(Strategy Pattern)

共同使用场景

  • 当需要动态地选择不同的操作(策略)并对对象集合中的元素执行这些操作时,策略模式和访问者模式可以协同使用。

协同原因

  • 策略模式用于定义一组算法或操作,并使其可以互换。
  • 访问者模式用于将这些操作应用于对象结构中的元素。

示例

  • 在电商系统中,策略模式可以定义不同的支付策略(如信用卡支付、支付宝支付等)。访问者模式可以用来遍历订单项并应用不同的支付策略。

4. 命令模式(Command Pattern)

共同使用场景

  • 当需要将操作封装为命令对象,并支持撤销、重做等操作时,命令模式和访问者模式可以协同使用。

协同原因

  • 命令模式用于将操作封装为对象,使其可以被传递、存储和执行。
  • 访问者模式用于将这些命令应用到对象结构中的元素。

示例

  • 在图形编辑器中,命令模式可以用来封装绘制、移动等操作。访问者模式可以用来将这些操作应用到不同的图形元素上。

5. 装饰器模式(Decorator Pattern)

共同使用场景

  • 当需要动态地为对象添加功能,并对这些对象执行操作时,装饰器模式和访问者模式可以协同使用。

协同原因

  • 装饰器模式用于动态地为对象添加职责。
  • 访问者模式用于对这些对象执行操作。

示例

  • 在文本编辑器中,装饰器模式可以用来为文本对象添加不同的样式(如加粗、斜体等)。访问者模式可以用来对这些文本对象进行渲染操作。

6. 模板方法模式(Template Method Pattern)

共同使用场景

  • 当需要定义一个算法的骨架,并将某些步骤延迟到子类中实现时,模板方法模式和访问者模式可以协同使用。

协同原因

  • 模板方法模式用于定义算法的骨架。
  • 访问者模式用于实现具体的操作步骤。

示例

  • 在报告生成系统中,模板方法模式可以定义生成报告的算法步骤(如生成标题、生成正文、生成结尾等)。访问者模式可以用来实现每个步骤的具体操作。

总结

访问者模式通常与其他模式协同使用,以增强其功能或解决特定的问题。常见的协同使用模式包括:

  • 组合模式:用于构建复杂的层次结构。
  • 迭代器模式:用于遍历对象集合。
  • 策略模式:用于动态选择操作。
  • 命令模式:用于封装操作并支持撤销、重做。
  • 装饰器模式:用于动态添加功能。
  • 模板方法模式:用于定义算法骨架。

通过这些模式的协同使用,访问者模式可以更好地适应复杂的需求场景,提供更高的灵活性和扩展性。

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

家用电器销售系统|Java|SSM|JSP|

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html 5⃣️数据库可…

ChatGPT 与 AGI:人工智能的当下与未来走向全解析

在人工智能的浩瀚星空中&#xff0c;AGI&#xff08;通用人工智能&#xff09;无疑是那颗最为璀璨且备受瞩目的星辰。OpenAI 对 AGI 的定义为“在最具经济价值的任务中超越人类的高度自治系统”&#xff0c;并勾勒出其发展的五个阶段&#xff0c;当下我们大多处于以 ChatGPT 为…

28.<Spring博客系统⑤(部署的整个过程(CentOS))>

引入依赖 Spring-boot-maven-plugin 用maven进行打包的时候必须用到这个插件。看看自己pom.xml中有没有这个插件 并且看看配置正确不正常。 注&#xff1a;我们这个项目打的jar包在30MB左右。 <plugin><groupId>org.springframework.boot</groupId><artif…

win11 vs2022 opencv 4.10使用vs Image Watch插件实时可视化内存mat对象

这个本来是非开源工业软件HALCON的一个功能&#xff0c;方便提升图像识别开发效率。原以为opencv没有&#xff0c;需要通过进程间共享内存的方式去实现。 结果在官网帮助文档中发现已经提供了。 opencv 4.10帮助文档https://docs.opencv.org/4.10.0/index.htmlOpenCV Tutorial…

用Python操作字节流中的Excel工作簿

Python能够轻松地从字节流中加载文件&#xff0c;在不依赖于外部存储的情况下直接对其进行读取、修改等复杂操作&#xff0c;并最终将更改后的文档保存回字节串中。这种能力不仅极大地提高了数据处理的灵活性&#xff0c;还确保了数据的安全性和完整性&#xff0c;尤其是在网络…

uni-app微信小程序如何使用高德地图。通过经纬度获取所在城市,涉及到授权获取地理位置权限

高德地图官方是这样介绍的使用方法可以参考&#xff1a;入门指南-微信小程序插件 | 高德地图API 我再介绍一下我得具体应用。 1&#xff0c;首先要在申请高德地图开放平台得账号。然后在这个账号中申请一个应用。类型选择微信小程序。 我的应用 | 高德控制台 获取Key-创建工…

YOLOv5部署到web端(flask+js简单易懂)

文章目录 前言最终实现效果图后端实现 主界面检测函数检测结果显示 前端实现 主界面(index.html&#xff09;显示图片界面 总结 前言 最近&#xff0c;老板让写一个程序把yolov5检测模型部署到web端&#xff0c;在网页直接进行目标检测。经过1个星期的努力&#xff0c;终于实…

使用Locust对Redis进行负载测试

1.安装环境 安装redis brew install redis 开启redis服务 brew services start redis 停止redis服务 brew services stop redis 安装Python库 pip install locust redis 2.编写脚本 loadTest.py # codingutf-8 import json import random import time import redis …

C#-使用StbSharp库读写图片

一.StbSharp StbSharp是基于C/Stb图形处理库封装的C#接口,支持多种格式PNG/JPG等图片的处理. GitHub链接: GitHub - StbSharp/StbTrueTypeSharp: C# port of stb_truetype.hhttps://github.com/StbSharp/StbTrueTypeSharp二.使用StbSharp创建高度图 创建一张500*500的高度图PN…

单周期CPU电路设计

1.实验目的 本实验旨在让学生通过设计一个简单的单周期 CPU 电路&#xff0c;深入理解 RISC-V 指令集的子集功能实现&#xff0c;掌握数字电路设计与实现的基本流程&#xff0c;包括指令解析、部件组合、电路设计以及功能仿真等环节&#xff0c;同时培养verilog HDL编程能力和…

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(五)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 五、 解释评估&#xff08;Explanation Evaluation&#xff09; 在前面的章节中&#xff0c;我们介绍了不同的解释技术和它们的用途&#…

UE5材质节点Camera Vector/Reflection Vector

Camera Vector相机向量&#xff0c;输出像素到相机的方向&#xff0c;结果归一化 会随着相机移动而改变 Reflection Vector 反射向量&#xff0c;物体表面法线反射到相机的方向&#xff0c;x和y和camera vector相反 配合hdr使用

使用Qt中的模型视图框架

本篇文章让你能够在阅读完之后&#xff0c;掌握Qt的模型视图框架的大致使用方法。 问题引入 在我们开发较小的软件的时候&#xff0c;我们可能不会注意到模型视图框架的作用。 因为我们的同一份的数据可能只会在同一个窗口中显示&#xff0c;不会存在数据在一个窗口中更新&a…

跟着逻辑先生学习FPGA-实战篇第一课 6-1 LED灯闪烁实验

硬件平台&#xff1a;征战Pro开发板 软件平台&#xff1a;Vivado2018.3 仿真软件&#xff1a;Modelsim10.6d 文本编译器&#xff1a;Notepad 征战Pro开发板资料 链接:https://pan.baidu.com/s/1AIcnaGBpNLgFT8GG1yC-cA?pwdx3u8 提取码:x3u8 1 知识背景 LED&#xff0c;又名…

2024国城杯 Web

这四道题目Jasper大佬都做了镜像可以直接拉取进行复现 https://jaspersec.top/2024/12/16/0x12%20%E5%9B%BD%E5%9F%8E%E6%9D%AF2024%20writeup%20with%20docker/ n0ob_un4er 这道题没有复现成功, 不知道为啥上传了文件, 也在 /tmp目录下生成了sess_PHPSESSID的文件, 但是就是…

【ArcGISPro/GeoScenePro】检查多光谱影像的属性并优化其外观

数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 操作 其他数据 检查影像的属性 熟悉检查您正在使用的栅格属性非常重要。

基于Docker基础与操作实战

6.1 Docker容器简介 Docker是一个开源的应用容器引擎&#xff0c;它基于Go语言并遵从Apache2.0 协议开源。 Docker是一个用于开发&#xff0c;交付和运行应用程序的开放平台。Docker能将应用程序与基础架构分开&#xff0c;从而可以快速交付软件。借助Docker&#xff0c;您可…

使用exe4j将jar转成exe、java打包exe

1、maven打包 需要配置以下插件&#xff0c;注意skip为false 插件配置中设置 <skip>true</skip> 时&#xff0c;实际上是告诉 Maven 在构建过程中跳过 spring-boot-maven-plugin 插件的执行。也就是说&#xff0c;Maven 在打包时不会将项目打包成可执行的 JAR 文…

MySQL 的事务与多版本并发控制(MVCC)的那些事

什么是事务原子性:一致性隔离性 问题1: 为什么MySQL要使用mvcc实现隔离性而不使用 锁 解决并发问题?持久性 问题2: MySQL 不是磁盘数据库吗,持久化为什么是 redo log 保证的?问题 3: redo log 储存了什么东西,持久化(崩溃恢复是怎么做的?)问题 4 : MySQL 的 bing log (二进制…

【单片机通讯协议】—— 常用的UART/I2C/SPI等通讯协议的基本原理与时序分析

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、通信基本知识1.1 MCU的参见外设1.2 通信的分类按基本的类型从传输方向上来分 二、UART&#xff08;串口通讯&#xff09;2.1 简介2.2 时序图分析2.3 UART的…