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 语…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
