访问者模式
1.相关文档
https://refactoring.guru/design-patterns/visitor
https://www.tutorialspoint.com/design_pattern/visitor_pattern.htm
2. 为什么要用访问者模式,解决了什么问题
2.1 问题场景
有一个无向图,目前的需求是把它广度优先遍历,把取出的数据存生成一个xml文件,有一天,需求变了,要求导出一个pdf文件,有一天又变了需求导出一个doc文档,后来又要求同时支持上面几个文件格式了。这个展出无向图数据的类要如何实现?每次都修改顶点的导出函数?违反了开-闭原则,而且每次修改都可能引出新的bug.
2.2 解决方案
针对上面的问题,如果把导出成文件这个行为封装成接口Visitor,其中的方法形参为顶点数据,其实现类完成各自的导出功能,顶点不再依赖具体实现,就可以解决上面的问题。这时顶点被抽象为Acceptor,把自己传送给Visitor.
这就是访问者模式的设计思想。把对象、复杂数据和业务、行为分离。访问者遍历受访者的数据并由访问者完成业务功能。
3.要点
3.1 优点
- 在数据不变的情况下,扩展Visitor就可扩展功能而不修改原数据类。
- 与迭代器模式相比,支持业务行为并且可扩展。
3.2 缺点
- 每增加一个新的数据,都要改变访问者接口及它的实现类。
4.类图
5.示例
5.1 访问者接口visitor.h
#ifndef __VISITOR__H__ #define __VISITOR__H__ class A1; class A2; struct Visitor{ virtual void visit(const A1 *a1) const = 0; virtual void visit(const A2 *a2) const = 0; virtual ~Visitor() = default; }; struct V1 : Visitor{ void visit(const A1 *a1) const override; void visit(const A2 *a2) const override; }; struct V2 : Visitor{ void visit(const A1 *a1) const override; void visit(const A2 *a2) const override; }; #endif //__VISITOR__H__
5.2 受访者接口acceptor.h
#ifndef __ACCEPTOR__H__ #define __ACCEPTOR__H__ struct Acceptor{ virtual void accept(const Visitor *v)const = 0; virtual ~Acceptor() = default; }; struct A1 : Acceptor{ void accept(const Visitor *vistor)const override; ~A1(); }; struct A2 : Acceptor{ void accept(const Visitor *vistor)const override; ~A2(); }; #endif //__ACCEPTOR__H__
5.3 复合数据类composite.h
#ifndef __COMPOSITE__H_ #define __COMPOSITE__H_ struct Composite{ vector<const Acceptor*> data; Composite(); ~Composite(); void accept(const Visitor *visitor); void add(const A1 *a1); void add(const A2 *a2); }; #endif //__COMPOSITE__H_
5.4 各类的实现main.cpp
#include <iostream> #include <vector> using namespace std; #include "visitor.h" #include "acceptor.h" #include "composite.h" //-------visitor------=====--------=-=-=-=-==-=-------=====--------------------==---- void V1::visit(const A1 * a1) const { cout << "V1 visit a1 ----->" << endl; } void V1::visit(const A2 * a2) const { cout << "V1 visit a2 ----->" << endl; } void V2::visit(const A1 * a1) const { cout << "V2 visit a1 =====>" << endl; } void V2::visit(const A2 * a2) const { cout << "V2 visit a2 =====>" << endl; } //-------acceptor------=====--------=-=-=-=-==-=-------=====--------------------==---- void A1::accept(const Visitor *visitor)const { visitor->visit(this); } A1::~A1(){ // cout << __func__ << endl; } void A2::accept(const Visitor *visitor)const { visitor->visit(this); } A2::~A2(){ // cout << __func__ << endl; } //-------composite------=====--------=-=-=-=-==-=-------=====--------------------==---- Composite::Composite(){ A1 *a1 = new A1(); A2 *a2 = new A2(); data.push_back(a1); data.push_back(a2); } Composite::~Composite(){ for (const Acceptor *acceptor : data){ delete acceptor; } data.clear(); } void Composite::accept(const Visitor *visitor){ for (const Acceptor *acceptor : data){ acceptor->accept(visitor); } } void Composite::add(const A1 * a1){ data.push_back(a1); } void Composite::add(const A2 * a2){ data.push_back(a2); } //main int main(int argc, char const *argv[]){ Composite composite; V1 vsitor1; //-----> V2 vsitor2; //=====> composite.accept(&vsitor1); composite.accept(&vsitor2); return 0; }
5.5 运行结果
两个访问者v1 和 v2 ,它们分别访问了composite对象,且产生了不同的结果。
V1 visit a1 -----> V1 visit a2 -----> V2 visit a1 =====> V2 visit a2 =====>
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2016-04-03 Android内存管理(12)*「实例」用Monitor 生成.hprof文件 并分析内存泄漏