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 语…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
