设计模式(17) 访问者模式(VISITOR) C++实现
意图:
表示一个作用于某对象结构的各元素的操作。它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作。
动机:
之前在学校的最后一个小项目就是做一个编译器,当时使用的就是访问者模式。
在静态分析阶段,将源程序表示为一个抽象语法树,编译器需要在抽象语法树的基础上实施某些操作以进行静态语义分析。可能需要定义许多操作以进行类型检查、代码优化、流程分析、检查变量是否在使用前被赋值,等等。
这个需求的特点是:要求对不同的节点进行不同的处理。
常规设计方法:不同的节点封装不同的操作。
缺点是,节点类型过多,将操作分散在各个节点类中会导致整个系统难以理解、维护和修改。增加新的操作要修改和重新编译所有的类。
改进:节点类独立于作用于其上的操作。
1 将相关操作封装在一个独立的对象(Visitor)中,并在遍历抽象语法树时将此对象传递给当前访问的元素。
2 当一个节点接受一个访问者时,该元素向访问者发送一个包含自身类信息的请求。该请求同时也将该元素本身作为一个参数。
3 访问者将对该元素执行该操作。
适用性:
在下列情况下使用Visitor模式:
一个对象结构包含很多类对象
需要对其中的对象进行很多不同的并且不相关的操作
对象很少改变,经常需要对其上的操作进行修改或新增
需要注意的一点是,如果对象结果和接口经常改变,那么会导致需要重定义所有访问者的接口,会导致很大的代价。所以这种情况还是在对象类中定义操作比较好。
结构:

协作:

示例代码:
背景:假设你的电脑出现问题了 ,拿到售后那边检测,售后告诉你必须拆机检测,检测过程通过两个技术人员依次负责不同功能的检测。
分析:本例中,两个负责不同功能检测的技术人员就是Visitor,而电脑的各个部件就是elements。
特点:电脑的部件是固定的,不会有太大的改变,但是如果一种检测方式没有找出问题的话,那么就需要增加检测项。符合访问者模式的特点。
//visit.h #ifndef VISITOR_H
#define VISITOR_H #include <iostream>
#include <string>
#include <vector> class Element;
class CPU;
class VideoCard;
class MainBoard; /*------------------*/
class Visitor {
public:
Visitor(std::string name) {
visitorName = name;
}
virtual void visitCPU( CPU* cpu ) {};
virtual void visitVideoCard( VideoCard* videoCard ) {};
virtual void visitMainBoard( MainBoard* mainBoard ) {}; std::string getName() {
return this->visitorName;
};
private:
std::string visitorName;
}; class Element {
public:
Element( std::string name ) {
eleName = name;
}
virtual void accept( Visitor* visitor ) {}; virtual std::string getName() {
return this->eleName;
}
private:
std::string eleName;
}; /*----------- Elements -------------*/ class CPU : public Element {
public:
CPU(std::string name) : Element(name) {} void accept(Visitor* visitor) {
visitor->visitCPU(this);
}
}; class VideoCard : public Element {
public:
VideoCard(std::string name) : Element(name) {} void accept(Visitor* visitor) {
visitor->visitVideoCard(this);
}
}; class MainBoard : public Element {
public:
MainBoard(std::string name) : Element(name) {} void accept(Visitor* visitor) {
visitor->visitMainBoard(this);
}
}; /*----------- ConcreteVisitor -------------*/ class CircuitDetector : public Visitor {
public:
CircuitDetector(std::string name) : Visitor(name) {} // checking cpu
void visitCPU( CPU* cpu ) {
std::cout << Visitor::getName() << " is checking CPU's circuits.(" << cpu->getName()<<")" << std::endl;
} // checking videoCard
void visitVideoCard( VideoCard* videoCard ) {
std::cout << Visitor::getName() << " is checking VideoCard's circuits.(" << videoCard->getName()<<")" << std::endl;
} // checking mainboard
void visitMainBoard( MainBoard* mainboard ) {
std::cout << Visitor::getName() << " is checking MainBoard's circuits.(" << mainboard->getName() <<")" << std::endl;
} }; class FunctionDetector : public Visitor {
public:
FunctionDetector(std::string name) : Visitor(name) {}
virtual void visitCPU( CPU* cpu ) {
std::cout << Visitor::getName() << " is check CPU's function.(" << cpu->getName() << ")"<< std::endl;
} // checking videoCard
void visitVideoCard( VideoCard* videoCard ) {
std::cout << Visitor::getName() << " is checking VideoCard's function.(" << videoCard->getName()<< ")" << std::endl;
} // checking mainboard
void visitMainBoard( MainBoard* mainboard ) {
std::cout << Visitor::getName() << " is checking MainBoard's function.(" << mainboard->getName() << ")"<< std::endl;
}
}; /*------------------------*/ class Computer {
public:
Computer(CPU* cpu,
VideoCard* videocard,
MainBoard* mainboard) {
elementList.push_back(cpu);
elementList.push_back(videocard);
elementList.push_back(mainboard);
};
void Accept(Visitor* visitor) {
for( std::vector<Element*>::iterator i = elementList.begin(); i != elementList.end(); i++ )
{
(*i)->accept(visitor);
}
};
private:
std::vector<Element*> elementList;
}; #endif
// main.cpp
#include "visitor.h"
int main() {
CPU* cpu = new CPU("Intel CPU");
VideoCard* videocard = new VideoCard("XXX video card");
MainBoard* mainboard = new MainBoard("HUAWEI mainboard");
Computer* myComputer = new Computer(cpu, videocard, mainboard);
CircuitDetector* Dan = new CircuitDetector("CircuitDetector Dan");
FunctionDetector* Tom = new FunctionDetector("FunctionDetector Tom");
std::cout << "\nStep 1: Dan is checking computer's circuits." << std::endl;
myComputer->Accept(Dan);
std::cout << "\nStep 2: Tom is checking computer's functions." << std::endl;
myComputer->Accept(Tom);
system("Pause");
return 0;
}
运行截图:

参考资料:
《设计模式:可复用面向对象软件的基础》
设计模式(17) 访问者模式(VISITOR) C++实现的更多相关文章
- 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)
原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...
- 二十四种设计模式:访问者模式(Visitor Pattern)
访问者模式(Visitor Pattern) 介绍表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 示例有一个Message实体类,某些对象对 ...
- [设计模式] 23 访问者模式 visitor Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问 ...
- 【设计模式】—— 访问者模式Visitor
前言:[模式总览]——————————by xingoo 模式意图 对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同.此时,就是访问者模式的典型应用了. 应用场景 1 不同的子类 ...
- 行为型设计模式之访问者模式(Visitor)
结构 意图 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 适用性 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依 ...
- 设计模式 ( 二十 ) 访问者模式Visitor(对象行为型)
设计模式 ( 二十 ) 访问者模式Visitor(对象行为型) 1.概述 在软件开发过程中,对于系统中的某些对象,它们存储在同一个集合collection中,且具有不同的类型,而且对于该集合中的对象, ...
- 访问者模式 Visitor 行为型 设计模式(二十七)
访问者模式 Visitor <侠客行>是当代作家金庸创作的长篇武侠小说,新版电视剧<侠客行>中,开篇有一段独白: “茫茫海外,传说有座侠客岛,岛上赏善罚恶二使,每隔十年 ...
- 设计模式:访问者(Visitor)模式
设计模式:访问者(Visitor)模式 一.前言 什么叫做访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+ ...
- 北风设计模式课程---访问者模式(Visitor)
北风设计模式课程---访问者模式(Visitor) 一.总结 一句话总结: 设计模式是日常问题的经验总结方案,所以学好设计模式对日常出现的问题可以有很好的解决. 访问者设计模式有点神似 抽象工厂模式, ...
随机推荐
- 十分有趣的this指向题
var num=5;//9 =>++this.num=>10 var obj={ num:3, fn:function() { num = 9;//window.num=9 (functi ...
- java数据结构---------插入排序的实现
插入排序分为直接插入排序和希尔排序 插入排序 实现方法 //插入排序,按从小到大的顺序 public static void insertSort(int[] array){ int j,temp = ...
- 实例说明optimize table在优化mysql时很重要
今天在看CU的时候,发现有人问有关optimize来表优化的问题,当年因为这个问题,困扰我很长一段时间,今天有空我把这个问题,用实际数据来展示出来,让大家可以亲眼来看看,optimize table的 ...
- Comet事件分析
简介[ Introduction ] 使用APR或者NIO API作为连接器的基础,Tomcat能够提供一些在阻塞IO之上的有效扩展,用于支持Servlet API. [ With usage of ...
- Linux笔记-Linux命令初解2
在看linux过程中,文件属性管理是一个难点,因而作为初学者的我来说,我直接将其放在后面来慢慢研究,因而我个人觉得先学习后面一些知识点之后,回过头来将一些你所不懂的去解透,这是极好的意见事情.对了,我 ...
- 单独烧录kernel
使用fastboot: adb reboot bootloader //adb 进入BootLoader(fastboot是BootLoader的命令) fastboot flash boot boo ...
- LOJ #6022. 重组病毒
Description 支持以下操作 1.access一个点 2.问一个点上面的重链的个数 3.换根 , 并access原来的根 Solution 对于重链个数 , 我们在 \(access\) 的时 ...
- 微信WeUI常见页面模板
购物车模板 就是popup弹层(css样式+js),还有slider滑动操作,还有增减的js 代码: <!DOCTYPE html> <html lang="zh-CN&q ...
- 微信WeUI扩展组件
主要包括 下拉刷新pullToRefresh downRefresh.html 主要的代码是$(document.body).pullToRefresh(); <div class=" ...
- 使用MUI框架,模拟手机端的下拉刷新,上拉加载操作。
套用mui官方文档的一句话:“开发者只需关心业务逻辑,实现加载更多数据即可”.真的是不错的框架. 想更多的了解这个框架:http://dev.dcloud.net.cn/mui/ 那么如何实现下拉刷新 ...