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

C++设计模式——Visitor访问者模式

一,访问者模式的定义

访问者模式是一种行为型设计模式,它允许开发者定义一系列操作,这些操作可以应用于同一个对象结构中的不同元素。访问者模式将算法与对象的结构分离,通过这种方式,访问者模式可以在不改变原有对象的前提下,定义新的操作。

访问者模式使得操作可以独立于数据结构而变化。

访问者模式在现实生活中的抽象实例:

游客参观:在旅游景区中,游客可以作为访问者,景区的各个景点作为被访问的元素。游客根据个人兴趣对不同的景点进行参观和了解。

医生查房:医生作为访问者根据病人的病情和需要,对不同的病人进行检查和治疗。

酒店服务员:服务员作为访问者,根据客户的需求和要求,对不同的客房进行打扫和服务。

财务审计员:财务审计员作为访问者,根据企业的财务情况和政策要求,对不同的部门和账目进行审计和核对。

二,访问者模式的结构

访问者模式主要包含以下组件:

1.访问者(Visitor):

访问者声明了访问对象结构的统一方法,该方法接收一个元素对象作为参数。

2.具体访问者(Concrete Visitor):

具体访问者是实现访问者接口的具体类,它包含了访问操作的实现细节,通过调用不同的具体访问者,可以实现不同的访问操作。

3.元素(Element):

元素声明了接受访问者访问时的操作。

4.具体元素(Concrete Element):

具体元素是实现元素接口的具体类,它包含了接收访问时所进行的具体操作细节。

5.对象结构(Object Structure):

对象结构是元素的集合,它提供了访问元素的统一接口,使访问者可以遍历所有的元素。

访问者和元素的关系:访问者定义了可以访问和操作元素的方法,而元素则提供了一个接受访问者的方法。

组件之间的工作步骤如下:

1.客户端通过调用访问者的方法来访问对象结构。

2.对象结构将自身作为参数传递给访问者。

3.访问者根据需要调用元素的方法进行操作。

4.元素执行与访问者相关的操作,也可以将自身作为参数传递给访问者。

对应UML类图:

三,访问者模式代码样例

伪代码样例:

class concreteElement_1;
class concreteElement_2;class visitor
{
public:virtual void visit(concreteElement_1& el) = 0;virtual void visit(concreteElement_2& el) = 0;
};class concreteVisitor : public visitor
{
public:virtual void visit(concreteElement_1& el) override{// Do something};virtual void visit(concreteElement_2& el) override{// Do something};
};class element
{
public:virtual void accept(visitor& v) = 0;
};class concreteElement_1 : public element
{
public:virtual void accept(visitor& v) override{v.visit(*this);}
};class concreteElement_2 : public element
{
public:virtual void accept(visitor& v) override{v.visit(*this);}
};

完整代码样例:

#include <iostream>class ElementA;
class ElementB;class Visitor {
public:virtual void visit(ElementA* element) = 0;virtual void visit(ElementB* element) = 0;
};class Element {
public:virtual void accept(Visitor* visitor) = 0;
};class ElementA: public Element {
public:void accept(Visitor* visitor) override {visitor->visit(this);}void operationA() {std::cout << "OperationA called on ElementA." << std::endl;}
};class ElementB: public Element {
public:void accept(Visitor* visitor) override {visitor->visit(this);}void operationB() {std::cout << "OperationB called on ElementB." << std::endl;}
};class ConcreteVisitor: public Visitor{
public:void visit(ElementA* element) override {std::cout << "visits ElementA." << std::endl;element->operationA();}void visit(ElementB* element) override {std::cout << "visits ElementB." << std::endl;element->operationB();}
};int main() {ElementA elementA;ElementB elementB;ConcreteVisitor visitor;Element* elements[] = { &elementA, &elementB };for (Element* element : elements) {element->accept(&visitor);}return 0;
}

运行结果:

visits ElementA.
OperationA called on ElementA.
visits ElementB.
OperationB called on ElementB.

四,访问者模式的应用场景

XML解析:XML文档的元素可以有各种各样的类型,可以编写一个通用的遍历函数,对所有类型的元素进行一致的操作。

图形处理:开发一个通用的处理器可以针对所有图元(矩形、圆形、线条等)进行统一的操作。

游戏状态管理:基于访问者模式开发一个状态机来管理各种角色的行为。

软件架构解耦:当组件的操作依赖于具体的数据类型时,访问者模式可以帮助降低组件之间的耦合度。

编译器开发:在词法分析阶段,使用访问者模式遍历源代码,可以根据不同的语法结构进行相应的处理。

五,访问者模式的优缺点

访问者模式的优点:

有助于将算法的关注点与其操作的对象的结构分开。

符合"开闭原则", 引入新的操作(访问者)时,无需修改被访问元素的现有代码。

使用具体的类来封装特定的操作,方便维护。

使得被调用的方法可以在代码运行期间动态修改。

访问者模式的缺点:

如果为简单的操作定义单独的类,会使得代码更复杂。

存在安全隐患,被访问的元素可能会向访问者公开其内部结构。

当访问操作很多且频繁时,性能开销大。

六,代码实战

Demo1:模拟的XML解析器

#include <iostream>
#include <vector>class Element;
class Text;class Visitor {
public:virtual void visit(Element& element) = 0;virtual void visit(Text& text) = 0;
};class Node {
public:virtual void accept(Visitor& visitor) = 0;
};class Element : public Node{
public:std::string name;Element(const std::string& n) : name(n) {}void accept(Visitor& visitor) override {visitor.visit(*this);}
};class Text: public Node{
public:std::string content;Text(const std::string& c) : content(c) {}void accept(Visitor& visitor) override {visitor.visit(*this);}
};class XmlDocument {
public:std::vector<Node*> nodes;void addNode(Node* node) {nodes.push_back(node);}void accept(Visitor& visitor) {for (auto node : nodes) {node->accept(visitor);}}
};class ElementCounter : public Visitor {
public:int elementCount = 0;void visit(Element& element) override {std::cout << "Found element: " << element.name << std::endl;elementCount++;}void visit(Text& text) override {std::cout << "Found text: " << text.content << std::endl;}
};int main() {XmlDocument document;Element element1("div");Text text("Hello, world!");Element element2("p");document.addNode(&element1);document.addNode(&text);document.addNode(&element2);ElementCounter counter;document.accept(counter);std::cout << "Total elements found: " << counter.elementCount << std::endl;return 0;
}

运行结果:

Found element: div
Found text: Hello, world!
Found element: p
Total elements found: 2

Demo2:模拟的AST抽象语法树

#include <iostream>class Number;
class BinaryOperation;class ExpressionVisitor {
public:virtual double visit(Number& number) = 0;virtual double visit(BinaryOperation& binaryOperation) = 0;
};class Expression {
public:virtual double accept(ExpressionVisitor& visitor) = 0;
};class Number : public Expression {
public:double value;Number(double v) : value(v) {}double accept(ExpressionVisitor& visitor) override {return visitor.visit(*this);}
};class BinaryOperation : public Expression {
public:char operation;Expression* left;Expression* right;BinaryOperation(char op, Expression* l, Expression* r) : operation(op), left(l),  right(r) {}double accept(ExpressionVisitor& visitor) override {return visitor.visit(*this);}
};class ExpressionEvaluator : public ExpressionVisitor {
public:double visit(Number& number) override {return number.value;}double visit(BinaryOperation& binaryOperation) override {double left = binaryOperation.left->accept(*this);double right = binaryOperation.right->accept(*this);switch (binaryOperation.operation) {case '+':return left + right;case '-':return left - right;case '*':return left * right;case '/':if (right != 0) {return left / right;}else {std::cerr << "Division by zero" << std::endl;return 0;}default:std::cerr << "Invalid operation: " << binaryOperation.operation <<  std::endl;return 0;}}
};int main() {Number num1(5.0);Number num2(3.0);Number num3(2.0);BinaryOperation expr1('+', &num1, &num2);BinaryOperation expr2('*', &expr1, &num3);ExpressionEvaluator evaluator;double result = expr2.accept(evaluator);std::cout << "Result: " << result << std::endl;return 0;
}

运行结果:

Result: 16

七,参考阅读

https://cpppatterns.com/

https://www.geeksforgeeks.org/visitor-method-design-patterns-in-c/

https://www.modernescpp.com/index.php/the-visitor-pattern/

https://softwarepatterns.com/cpp/visitor-software-pattern-cpp-example

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • MySQL索引的深入学习与应用
  • pip install “git+https://xxx“报错error: subprocess-exited-with-error
  • C++编程语言:基础设施:函数(Bjarne Stroustrup)
  • React项目中使用发布订阅模式
  • PMP--一模--解题--11-20
  • Hive是什么?
  • 缓存预热/雪崩/穿透/击穿
  • 828华为云征文 | Flexus X的力量,驱动Halo博客在云端飞驰
  • 你都学会栈和队列了赶紧手搓一个对象池吧!!!(超详细,超简单适合新手宝宝学习)
  • 跨系统环境下LabVIEW程序稳定运行
  • CSP-J 之C++常用英文缩写
  • minio的下载和springboot整合minio使用
  • Docker容器技术1——docker基本操作
  • 线性代数 第七讲 二次型_标准型_规范型_坐标变换_合同_正定二次型详细讲解_重难点题型总结
  • 天童教育:课外阅读图书推荐
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • CSS实用技巧干货
  • Elasticsearch 参考指南(升级前重新索引)
  • es的写入过程
  • Java,console输出实时的转向GUI textbox
  • js继承的实现方法
  • js如何打印object对象
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • Vue UI框架库开发介绍
  • 创建一种深思熟虑的文化
  • 代理模式
  • 回流、重绘及其优化
  • 记一次删除Git记录中的大文件的过程
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 深入 Nginx 之配置篇
  • 使用Gradle第一次构建Java程序
  • 数据可视化之 Sankey 桑基图的实现
  • 微信开放平台全网发布【失败】的几点排查方法
  • 线性表及其算法(java实现)
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • 阿里云重庆大学大数据训练营落地分享
  • ‌分布式计算技术与复杂算法优化:‌现代数据处理的基石
  • # SpringBoot 如何让指定的Bean先加载
  • #微信小程序(布局、渲染层基础知识)
  • (2024最新)CentOS 7上在线安装MySQL 5.7|喂饭级教程
  • (AngularJS)Angular 控制器之间通信初探
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (php伪随机数生成)[GWCTF 2019]枯燥的抽奖
  • (补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (十六)一篇文章学会Java的常用API
  • (四)stm32之通信协议
  • (算法)区间调度问题
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)C#调用WebService 基础
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .net dataexcel winform控件 更新 日志
  • .Net mvc总结