当前位置: 首页 > news >正文

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

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

访问者模式的核心思想

访问者模式的主要思想是将对象结构的操作封装到访问者中。对象结构中的每个元素都可以接受一个访问者,并让这个访问者对自己进行操作。这使得我们可以很方便地增加新的操作,而不需要修改对象的类。

访问者模式的主要组成部分包括:

  1. 访问者接口(Visitor):为每个对象结构中的元素定义访问操作。每个操作方法对应一个元素类型。

  2. 具体访问者(Concrete Visitor):实现访问者接口,定义每个元素对应的具体操作。

  3. 元素接口(Element):定义接受访问者的方法。这个方法通常会将自身作为参数传递给访问者。

  4. 具体元素(Concrete Element):实现元素接口,并在接受访问者时调用访问者的相应操作。

  5. 对象结构(Object Structure):通常是一个包含多个元素的集合,负责遍历这些元素,并让它们接受访问者。

访问者模式的应用场景

  1. 需要对类的对象结构进行复杂操作:例如编译器中的语法树、绘图系统中的图形对象层次等。访问者模式允许你将复杂的操作分离出来,而不影响对象结构。

  2. 不方便修改对象结构:当对象结构比较稳定,频繁添加新行为时,访问者模式可以让你在不修改这些对象类的前提下,增加新的操作。

  3. 操作与结构的分离:当你希望将操作与对象结构进行解耦时,访问者模式非常适用。

访问者模式的示例代码

假设我们有一个对象结构,包含不同类型的形状,如圆形、矩形、三角形等。我们希望对这些形状进行不同的操作,例如计算面积、绘制形状等。访问者模式可以帮助我们在不修改形状类的前提下添加这些操作。

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接口:定义了访问不同类型形状的操作(visitCirclevisitRectanglevisitTriangle)。

  • Shape接口:定义了接受访问者的方法(accept),每个具体的形状都会调用访问者的相应方法,并将自身传递给访问者。

  • 具体元素类(Circle、Rectangle、Triangle):这些类实现了Shape接口,并在accept方法中调用访问者的具体方法。

  • AreaVisitor和DrawVisitor类:这两个具体的访问者实现了访问者接口,用于计算形状的面积和绘制形状。每个访问者都提供了不同的功能,而不需要修改Shape类。

  • ShapeCollection类:这是一个对象结构,包含多个Shape对象,并让访问者依次访问每个形状。

访问者模式的优点

  • 增加操作非常容易:当需要对对象结构中的元素添加新的操作时,只需要增加新的访问者,而不需要修改已有的类,这符合开闭原则。

  • 分离关注点:访问者模式将不同操作(如计算面积、绘制形状等)分离到不同的访问者中,从而避免了将操作逻辑分散到元素类中,提高了系统的可维护性。

  • 符合单一职责原则:每个访问者只专注于一种操作,不会影响对象结构的定义和职责。

访问者模式的缺点

  • 违反了依赖倒置原则:访问者模式要求元素类依赖访问者接口,这在某种程度上违反了依赖倒置原则。因为对象结构中的元素需要接受访问者,这使得元素类必须知道访问者的存在。

  • 增加了复杂性:当对象结构中的元素种类较多时,访问者接口和访问者类会变得非常庞大,尤其是当每个访问者都需要实现多个访问方法时,复杂度会增加。

  • 元素类的变化困难:如果对象结构中的元素类需要频繁变化(如添加新的字段或属性),那么每个访问者类都需要修改,增加了维护成本。

适合使用访问者模式的情况

  • 需要对类结构中的对象添加新行为:如果频繁需要对类结构中的对象添加新的操作,访问者模式是一个很好的选择,因为你可以通过创建新的访问者来实现新行为,而不需要修改原有的类。

  • 稳定的类结构:访问者模式适合对象结构较为稳定、不频繁修改的情况,因为它通过增加访问者来扩展操作,而不是通过修改对象结构本身。

不适合使用访问者模式的情况

  • 对象结构频繁变化:如果对象结构中的类经常发生变化(例如添加新类型的元素或修改现有元素),访问者模式就不太适合,因为每次对象结构变化都需要修改所有访问者。

  • 简单系统:对于系统较为简单的情况,访问者模式会增加不必要的复杂性。

Qt中的访问者模式应用

在Qt开发中,访问者模式可以应用于复杂的UI组件、绘图系统或处理层次化对象的场景。例如,Qt的绘图系统中,可以有不同的图形元素(如圆形、矩形等),并且可能需要对这些图形元素进行不同的操作(如渲染、计算面积、检测碰撞等)。访问者模式可以帮助你将这些操作分离到不同的访问者中,从而提高系统的灵活性。

总结

访问者模式通过将操作与对象结构分离,使得你可以在不修改类结构的前提下增加新的操作。它非常适合那些对象结构相对稳定,但需要频繁添加操作的场景。尽管访问者模式在扩展新操作时非常灵活,但当对象结构变化较频繁时,可能会增加维护的复杂性。

相关文章:

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

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

手机在网状态的详细应用场景有哪些?

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

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&#xff0c;-v表示目录映射&#xff0c;-p时端口映射&#xff0c;把宿主机目录挂载到容器中 docker run -itd -p 80:80 -v /data/webapps/www/:/usr/share/nginx/html/ --nameweb02 nginx:latest 此时我们在发布网站时只需要放在宿主机的目录里就可以了 解…...

如何用ChatGPT 8小时写出一篇完整论文(附完整提示词)

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

AWS MySQL 升级(三)—— TAZ - 近0停机的小版本升级方案

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

Redis的应用以及Redis工具类的封装

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

E系列I/O模块在锂电装备制造系统的应用

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

ElasticsearchClient入门指南

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

软考中级笔记

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

学习python自动化——pytest单元测试框架

一、什么是pytest 单元测试框架&#xff0c;unittest&#xff08;python自带的&#xff09;&#xff0c;pytest&#xff08;第三方库&#xff09;。 用于编写测试用例、收集用例、执行用例、生成测试结果文件&#xff08;html、xml&#xff09; 1.1、安装pytest pip instal…...

定位、地图建立及管理合集

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

uniAPP是否可以做php语言书写后端的前端开发?

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

柒拾伍- AI内容农场生产文章自动发布至公众号 (一)

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

java.util.function Function<T, R>

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

Allegro在PCB上开槽的三种方法操作指导

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

Docker:快速部署

docker安装&#xff1a; ​‌​‬&#xfeff;​&#xfeff;⁠​‍‬​‍‬‬‌​‬‬‬​&#xfeff;⁠​‍​​‌‬‌&#xfeff;​​​​​​‌​​​​⁠​‍⁠‌安装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…...

自动化分析背后,一站式数据分析平台!

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

掌握 WPF 开发:基础、数据绑定与自定义控件

WPF&#xff08;Windows Presentation Foundation&#xff09;是用于构建现代桌面应用程序的强大框架。它通过 XAML&#xff08;Extensible Application Markup Language&#xff09;与丰富的控件体系&#xff0c;提供了灵活的 UI 开发方式。本文将介绍 WPF 的基础知识、XAML 语…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...