Qt C++设计模式->访问者模式
访问者模式(Visitor Pattern)是一种行为型设计模式,它将操作与对象结构分离,使得你可以在不改变对象结构的前提下定义作用于这些对象的新操作。访问者模式通过引入一个访问者对象,允许你在不修改类的前提下向已有类添加新的行为。
访问者模式的核心思想
访问者模式的主要思想是将对象结构的操作封装到访问者中。对象结构中的每个元素都可以接受一个访问者,并让这个访问者对自己进行操作。这使得我们可以很方便地增加新的操作,而不需要修改对象的类。
访问者模式的主要组成部分包括:
-
访问者接口(Visitor):为每个对象结构中的元素定义访问操作。每个操作方法对应一个元素类型。
-
具体访问者(Concrete Visitor):实现访问者接口,定义每个元素对应的具体操作。
-
元素接口(Element):定义接受访问者的方法。这个方法通常会将自身作为参数传递给访问者。
-
具体元素(Concrete Element):实现元素接口,并在接受访问者时调用访问者的相应操作。
-
对象结构(Object Structure):通常是一个包含多个元素的集合,负责遍历这些元素,并让它们接受访问者。
访问者模式的应用场景
-
需要对类的对象结构进行复杂操作:例如编译器中的语法树、绘图系统中的图形对象层次等。访问者模式允许你将复杂的操作分离出来,而不影响对象结构。
-
不方便修改对象结构:当对象结构比较稳定,频繁添加新行为时,访问者模式可以让你在不修改这些对象类的前提下,增加新的操作。
-
操作与结构的分离:当你希望将操作与对象结构进行解耦时,访问者模式非常适用。
访问者模式的示例代码
假设我们有一个对象结构,包含不同类型的形状,如圆形、矩形、三角形等。我们希望对这些形状进行不同的操作,例如计算面积、绘制形状等。访问者模式可以帮助我们在不修改形状类的前提下添加这些操作。
1. 定义访问者接口、元素接口和具体实现类
#include <QDebug>
#include <QString>
#include <QList>// 前向声明
class Circle;
class Rectangle;
class Triangle;// 访问者接口:定义访问每个元素的操作
class Visitor {
public:virtual void visitCircle(Circle* circle) = 0;virtual void visitRectangle(Rectangle* rectangle) = 0;virtual void visitTriangle(Triangle* triangle) = 0;virtual ~Visitor() = default;
};// 元素接口:定义接受访问者的方法
class Shape {
public:virtual void accept(Visitor* visitor) = 0;virtual ~Shape() = default;
};// 具体元素类:圆形
class Circle : public Shape {
private:int radius;public:Circle(int r) : radius(r) {}int getRadius() const {return radius;}void accept(Visitor* visitor) override {visitor->visitCircle(this); // 让访问者访问自己}
};// 具体元素类:矩形
class Rectangle : public Shape {
private:int width, height;public:Rectangle(int w, int h) : width(w), height(h) {}int getWidth() const {return width;}int getHeight() const {return height;}void accept(Visitor* visitor) override {visitor->visitRectangle(this); // 让访问者访问自己}
};// 具体元素类:三角形
class Triangle : public Shape {
private:int base, height;public:Triangle(int b, int h) : base(b), height(h) {}int getBase() const {return base;}int getHeight() const {return height;}void accept(Visitor* visitor) override {visitor->visitTriangle(this); // 让访问者访问自己}
};// 具体访问者类:计算形状面积
class AreaVisitor : public Visitor {
public:void visitCircle(Circle* circle) override {int radius = circle->getRadius();double area = 3.14 * radius * radius;qDebug() << "Circle area:" << area;}void visitRectangle(Rectangle* rectangle) override {int area = rectangle->getWidth() * rectangle->getHeight();qDebug() << "Rectangle area:" << area;}void visitTriangle(Triangle* triangle) override {double area = 0.5 * triangle->getBase() * triangle->getHeight();qDebug() << "Triangle area:" << area;}
};// 具体访问者类:绘制形状
class DrawVisitor : public Visitor {
public:void visitCircle(Circle* circle) override {qDebug() << "Drawing a circle with radius" << circle->getRadius();}void visitRectangle(Rectangle* rectangle) override {qDebug() << "Drawing a rectangle with width" << rectangle->getWidth() << "and height" << rectangle->getHeight();}void visitTriangle(Triangle* triangle) override {qDebug() << "Drawing a triangle with base" << triangle->getBase() << "and height" << triangle->getHeight();}
};// 对象结构:形状集合
class ShapeCollection {
private:QList<Shape*> shapes;public:void addShape(Shape* shape) {shapes.append(shape);}void accept(Visitor* visitor) {for (Shape* shape : shapes) {shape->accept(visitor); // 让每个形状接受访问者}}~ShapeCollection() {qDeleteAll(shapes);}
};// 使用示例
int main() {// 创建一些形状Shape* circle = new Circle(5);Shape* rectangle = new Rectangle(4, 6);Shape* triangle = new Triangle(3, 4);// 创建形状集合ShapeCollection shapeCollection;shapeCollection.addShape(circle);shapeCollection.addShape(rectangle);shapeCollection.addShape(triangle);// 创建访问者:计算面积AreaVisitor* areaVisitor = new AreaVisitor();qDebug() << "Calculating areas:";shapeCollection.accept(areaVisitor);// 创建访问者:绘制形状DrawVisitor* drawVisitor = new DrawVisitor();qDebug() << "\nDrawing shapes:";shapeCollection.accept(drawVisitor);// 清理内存delete areaVisitor;delete drawVisitor;return 0;
}
代码解析
-
Visitor接口:定义了访问不同类型形状的操作(
visitCircle
、visitRectangle
、visitTriangle
)。 -
Shape接口:定义了接受访问者的方法(
accept
),每个具体的形状都会调用访问者的相应方法,并将自身传递给访问者。 -
具体元素类(Circle、Rectangle、Triangle):这些类实现了
Shape
接口,并在accept
方法中调用访问者的具体方法。 -
AreaVisitor和DrawVisitor类:这两个具体的访问者实现了访问者接口,用于计算形状的面积和绘制形状。每个访问者都提供了不同的功能,而不需要修改
Shape
类。 -
ShapeCollection类:这是一个对象结构,包含多个
Shape
对象,并让访问者依次访问每个形状。
访问者模式的优点
-
增加操作非常容易:当需要对对象结构中的元素添加新的操作时,只需要增加新的访问者,而不需要修改已有的类,这符合开闭原则。
-
分离关注点:访问者模式将不同操作(如计算面积、绘制形状等)分离到不同的访问者中,从而避免了将操作逻辑分散到元素类中,提高了系统的可维护性。
-
符合单一职责原则:每个访问者只专注于一种操作,不会影响对象结构的定义和职责。
访问者模式的缺点
-
违反了依赖倒置原则:访问者模式要求元素类依赖访问者接口,这在某种程度上违反了依赖倒置原则。因为对象结构中的元素需要接受访问者,这使得元素类必须知道访问者的存在。
-
增加了复杂性:当对象结构中的元素种类较多时,访问者接口和访问者类会变得非常庞大,尤其是当每个访问者都需要实现多个访问方法时,复杂度会增加。
-
元素类的变化困难:如果对象结构中的元素类需要频繁变化(如添加新的字段或属性),那么每个访问者类都需要修改,增加了维护成本。
适合使用访问者模式的情况
-
需要对类结构中的对象添加新行为:如果频繁需要对类结构中的对象添加新的操作,访问者模式是一个很好的选择,因为你可以通过创建新的访问者来实现新行为,而不需要修改原有的类。
-
稳定的类结构:访问者模式适合对象结构较为稳定、不频繁修改的情况,因为它通过增加访问者来扩展操作,而不是通过修改对象结构本身。
不适合使用访问者模式的情况
-
对象结构频繁变化:如果对象结构中的类经常发生变化(例如添加新类型的元素或修改现有元素),访问者模式就不太适合,因为每次对象结构变化都需要修改所有访问者。
-
简单系统:对于系统较为简单的情况,访问者模式会增加不必要的复杂性。
Qt中的访问者模式应用
在Qt开发中,访问者模式可以应用于复杂的UI组件、绘图系统或处理层次化对象的场景。例如,Qt的绘图系统中,可以有不同的图形元素(如圆形、矩形等),并且可能需要对这些图形元素进行不同的操作(如渲染、计算面积、检测碰撞等)。访问者模式可以帮助你将这些操作分离到不同的访问者中,从而提高系统的灵活性。
总结
访问者模式通过将操作与对象结构分离,使得你可以在不修改类结构的前提下增加新的操作。它非常适合那些对象结构相对稳定,但需要频繁添加操作的场景。尽管访问者模式在扩展新操作时非常灵活,但当对象结构变化较频繁时,可能会增加维护的复杂性。
相关文章:

Qt C++设计模式->访问者模式
访问者模式(Visitor Pattern)是一种行为型设计模式,它将操作与对象结构分离,使得你可以在不改变对象结构的前提下定义作用于这些对象的新操作。访问者模式通过引入一个访问者对象,允许你在不修改类的前提下向已有类添加…...

手机在网状态的详细应用场景有哪些?
手机在网状态的详细应用场景涵盖了多个行业和领域,以下是一些具体的例子: 金融行业 风控审核:银行、贷款公司等金融机构在审批贷款或信用卡时,可以通过查询手机在网状态来验证申请人的手机号码是否真实有效,从而降低欺…...

Linux的kafka安装部署
1.kafka是一个分布式的,去中心化的,高吞吐低延迟,订阅模式的消息队列系统 确保要有jdk与zookeeper安装配置 2.下载kafka安装包 http://archive.apache.org/dist/kafka/2.4.1/kafka_2.12-2.4.1.tgz 此时可以wget http://archive.apache.org/dist/kafka/2.4.1/kafka_2.12-2.4.…...

docker部署虚拟机
创建新的容器web02,-v表示目录映射,-p时端口映射,把宿主机目录挂载到容器中 docker run -itd -p 80:80 -v /data/webapps/www/:/usr/share/nginx/html/ --nameweb02 nginx:latest 此时我们在发布网站时只需要放在宿主机的目录里就可以了 解…...

如何用ChatGPT 8小时写出一篇完整论文(附完整提示词)
今天教大家如何利用ChatGPT完成一篇完整的论文。只需要一个标题,剩下全部由ChatGPT完成。总耗时8小时。 阅前提醒: 1.适用人群:这个方法适合应付简单的学术任务,比如日常小论文或投稿一般期刊。但如果你要写高水平的论文…...

AWS MySQL 升级(三)—— TAZ - 近0停机的小版本升级方案
与AWS交流了解到的新方案,没有实际试过,所以本篇主要是些原理 一、 TAZ的含义 TAZ实际上就是 3 AZ,扩展一些就是 Multi-AZ DB Cluster,即在3个可用区部署DB,具备两个只读备用实例。 二、 TAZ的主要用途 1. 近0停机的小…...

Redis的应用以及Redis工具类的封装
在前后端分离的项目中,通过session和cookie的通信一般就失去效益了,即使这么做了也会产生著名的漏洞问题CSRF(Cross-site request forgery), 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。因…...

E系列I/O模块在锂电装备制造系统的应用
为了满足电池生产线对稳定性和生产效率的严苛要求,ZLG致远电子推出高速I/O应用方案,它不仅稳定可靠,而且速度快,能够迅速响应生产需求。 锂电池的生产工艺较为复杂,大致分为三个主要阶段:极片制作、电芯制作…...

ElasticsearchClient入门指南
在本教程中,我们将探讨如何使用Elasticsearch的官方Java客户端 - ElasticsearchClient。这个强大的工具允许您的Java应用程序与Elasticsearch集群进行交互,执行各种操作,如索引文档、执行搜索查询等。 前提条件 在开始之前,确保您的项目中已经包含了必要的依赖。您可以通过Ma…...

软考中级笔记
上午题 二 程序设计语言 6′ 1、编译程序和解释程序 解释器:翻译源程序时不生成独立的目标程序。 解释程序和源程序要参与到程序的运行过程中。 编译器:翻译时将源程序翻译成独立保存的目标程序。 机器上运行的是与源程序等价的目标程序。 源程序和编…...

学习python自动化——pytest单元测试框架
一、什么是pytest 单元测试框架,unittest(python自带的),pytest(第三方库)。 用于编写测试用例、收集用例、执行用例、生成测试结果文件(html、xml) 1.1、安装pytest pip instal…...

定位、地图建立及管理合集
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言机器人中常见的定位技术介绍一、直方图定位原理二、gnss、rtk定位原理三、【依赖二维激光雷达与全局地图的定位算法】amcl&am…...

uniAPP是否可以做php语言书写后端的前端开发?
UniAPP可以做PHP语言书写后端的前端开发。以下是关于这个问题的详细解答: 一、UniAPP与后端开发的关系 前后端分离:UniAPP作为一款跨平台开发框架,采用了前后端分离的开发模式。这意味着前端和后端的开发可以独立进行,互不影响。…...

柒拾伍- AI内容农场生产文章自动发布至公众号 (一)
一、内容农场 X AI 看过很多的新闻说 AI 产生 内容 污染网络,我也想试一下到底能污染成怎样。 然后为了编写爆款的内容,我选用这个 内容农场 的种子是来源于 微博热搜,让生长出来的垃圾文章更加火爆 涉及内容不能放 二、编写代码 关于代…...

java.util.function Function<T, R>
一、介绍 1、简介 Function<T, R> 是 Java 8 中的一个函数式接口,用于表示接受一个输入参数 T,并返回一个结果 R 的函数。Function接口中有一个抽象方法apply,用于定义函数的逻辑。Function接口通常用于将数据进行转换(处…...

Allegro在PCB上开槽的三种方法操作指导
Allegro如何在PCB上开槽的三种方法操作指导 当PCB有特殊设计要求的时候,需要在PCB上开槽,Allegro支持在PCB上开槽操作,具体操作如下 以下图为例,需要在这个板框中间开槽 开方形槽 选择shape add rect命令 画在Board Geometry-o…...

Docker:快速部署
docker安装: 安装Docker - 飞书云文档 (feishu.cn) docker命令解读 docker run -d \ > --name mysql \ > -p 33…...

如何指定this的值
1. 函数调用时指定 call -- 接收一个参数列表 apply -- 接收一个参数数组 2. 创建时指定this的值 bind -- 返回一个函数 传参方式与call相同 箭头函数 -- 其this值取决于上级作用域中的this值 <script>// 如何指定this的值// 1. 调用时指定this// 2. 创建时指定th…...

自动化分析背后,一站式数据分析平台!
自动化分析背后,一站式数据分析平台! 前言一站式数据分析平台 前言 在如今的企业管理中,数据已经不再是简单的存储和备份,而是成为了决策的核心驱动力。尤其是在面对海量数据的情况下,企业急需一个能够高效处理、分析…...

掌握 WPF 开发:基础、数据绑定与自定义控件
WPF(Windows Presentation Foundation)是用于构建现代桌面应用程序的强大框架。它通过 XAML(Extensible Application Markup Language)与丰富的控件体系,提供了灵活的 UI 开发方式。本文将介绍 WPF 的基础知识、XAML 语…...

PostgreSQL:生成-唯一主键id
1. 通过时间戳和随机数拼接生成 select TO_CHAR(NOW(), YYYYMMDDHH24MISS) || LPAD(FLOOR(RANDOM() * 1000000)::TEXT, 6, 0) AS unique_id解析: TO_CHAR(NOW(), ‘YYYYMMDDHH24MISS’):该部分将当前时间 (NOW()) 格式化为 YYYYMMDDHH24MISS 格式&#…...

Swift添加自定义字体
1. 在 Xcode 中添加自定义字体 首先,你需要将自定义字体文件(如 .ttf 或 .otf 文件)添加到项目中。 将字体文件拖放到项目的资源管理器中,确保选中了 "Copy items if needed"。打开 Info.plist 文件,添加一…...

Springboot 整合 Java DL4J 实现企业门禁人脸识别系统
🧑 博主简介:历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,…...

SSTI模板注入+CTF实例
参考文章: 一文了解SSTI和所有常见payload 以flask模板为例-腾讯云开发者社区-腾讯云 (tencent.com) python-flask模块注入(SSTI) - ctrl_TT豆 - 博客园 (cnblogs.com) ssti详解与例题以及绕过payload大全_ssti绕过空格-CSDN博客 1. SSTI(模板注入&…...

iPhone 16 Pro 拆解揭秘:设计改进与维修便利性
苹果最新推出的iPhone 16系列在许多方面都进行了更新和改进,而这次我们要聚焦的是其中的高端型号——iPhone 16 Pro。 这款手机不仅在性能上有所提升,在内部构造上也带来了不少变化,让我们一起来看看这些细节吧。 更容易进入的内部结构 对于…...

Java项目实战II养老||基于Java+Spring Boot+MySQL的社区智慧养老监护管理平台设计与实现(源码+数据库+文档)
目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着老龄化…...

利用FnOS搭建虚拟云桌面,并搭建前端开发环境(二)
利用FnOS搭建虚拟云桌面,并搭建前端开发环境 二 一、docker镜像二、环境配置三、核心环境配置流程文档 利用FnOS搭建虚拟云桌面,并搭建前端开发环境(一) 上一章安装了飞牛FnOS系统,界面如下,这一张配置前端…...

【Python】Qwen-VL-7B box
VLLM-Qwen2-VL-7B-Instruct import cv2# 读取图像 image_path haibaoA.png # 替换为图像的路径 image cv2.imread(image_path)# 定义框的坐标 (x1, y1) 是左上角,(x2, y2) 是右下角 x1, y1 200, 550 # 左上角坐标 x2, y2 799, 750 # 右下角坐标 h, w image.…...

echarts按需引入解决项目大小问题
背景: 按需加载缩减项目大小,提升项目性能和可用性 实现: 创建echarts.js main.js进行配置 页面中引用 效果 全量导入 按需加载:...

天气预报echarts
如上图,可以切换温度,降水量,风力风向和空气质量 <template><el-radio-group v-model"selectedData" change"updateChart"><el-radio-button label"temperature">温度</el-radio-butto…...