意图:

表示一个作用于某对象结构的各元素的操作。它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作。

动机:

之前在学校的最后一个小项目就是做一个编译器,当时使用的就是访问者模式。

在静态分析阶段,将源程序表示为一个抽象语法树,编译器需要在抽象语法树的基础上实施某些操作以进行静态语义分析。可能需要定义许多操作以进行类型检查、代码优化、流程分析、检查变量是否在使用前被赋值,等等。

这个需求的特点是:要求对不同的节点进行不同的处理。

常规设计方法:不同的节点封装不同的操作。

缺点是,节点类型过多,将操作分散在各个节点类中会导致整个系统难以理解、维护和修改。增加新的操作要修改和重新编译所有的类。

改进:节点类独立于作用于其上的操作。

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++实现的更多相关文章

  1. 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)

    原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...

  2. 二十四种设计模式:访问者模式(Visitor Pattern)

    访问者模式(Visitor Pattern) 介绍表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 示例有一个Message实体类,某些对象对 ...

  3. [设计模式] 23 访问者模式 visitor Pattern

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问 ...

  4. 【设计模式】—— 访问者模式Visitor

    前言:[模式总览]——————————by xingoo 模式意图 对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同.此时,就是访问者模式的典型应用了. 应用场景 1 不同的子类 ...

  5. 行为型设计模式之访问者模式(Visitor)

    结构 意图 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 适用性 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依 ...

  6. 设计模式 ( 二十 ) 访问者模式Visitor(对象行为型)

    设计模式 ( 二十 ) 访问者模式Visitor(对象行为型) 1.概述 在软件开发过程中,对于系统中的某些对象,它们存储在同一个集合collection中,且具有不同的类型,而且对于该集合中的对象, ...

  7. 访问者模式 Visitor 行为型 设计模式(二十七)

    访问者模式 Visitor    <侠客行>是当代作家金庸创作的长篇武侠小说,新版电视剧<侠客行>中,开篇有一段独白:  “茫茫海外,传说有座侠客岛,岛上赏善罚恶二使,每隔十年 ...

  8. 设计模式:访问者(Visitor)模式

    设计模式:访问者(Visitor)模式 一.前言    什么叫做访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+ ...

  9. 北风设计模式课程---访问者模式(Visitor)

    北风设计模式课程---访问者模式(Visitor) 一.总结 一句话总结: 设计模式是日常问题的经验总结方案,所以学好设计模式对日常出现的问题可以有很好的解决. 访问者设计模式有点神似 抽象工厂模式, ...

随机推荐

  1. mongo 语法 增删改查

    1.增 db.collection.insert()与db.collection.save() 都是增加,区别:save()遇到相同_id后,则更新此_id数据. 而insert()则报错 > ...

  2. 部署herko小记

    1 先执行如下 heroku run rake db:migrate

  3. Nginx 的信号控制

    摘自:Nginx服务器初识:Nginx启动.停止与信号控制 名称 功能 说明 HUP 重启   QUIT 从容关闭   TERM 快速关闭   INT 从容关闭   USR1 切换日志文件 通常用在切 ...

  4. memcached 学习笔记 5

     memcached installed on linux 使用的操作系统是centos6.5 (有桌面) 1 上传libebent和memcache到/usr/local/src [root@jt ...

  5. DFT,可测试性设计--概念理解

    工程会接触DFT.需要了解DFT知识,但不需要深入. 三种基本的测试(概念来自参考文档): 1. 边界扫描测试:Boundary Scan Test: 测试目标是IO-PAD,利用JTAG接口互连以方 ...

  6. 入门系列之使用fail2ban防御SSH服务器的暴力破解攻击

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由SQL GM发表于云+社区专栏 介绍 对于SSH服务的常见的攻击就是暴力破解攻击--远程攻击者通过不同的密码来无限次地进行登录尝试.当 ...

  7. Golang之并发资源竞争(互斥锁)

    并发本身并不复杂,但是因为有了资源竞争的问题,就使得我们开发出好的并发程序变得复杂起来,因为会引起很多莫名其妙的问题. package main import ( "fmt" &q ...

  8. [java] byte不能直接相加

    以下赋值语句将产生一个编译错误,原因是赋值运算符右侧的算术表达式在默认情况下的计算结果为 int 类型. // Error: conversion from int to byte:byte z =  ...

  9. SSIS教程:创建简单的ETL包 -- 4. 增加错误处理流程(Adding Error Flow Redirection)

    为了处理在转换过程中可能发生的错误,MicrosoftIntegration Services 允许根据每个组件和每个列来决定如何处理无法转换的数据. 可以选择忽略某些列中的失败.重定向整个失败的行或 ...

  10. java虚拟机的内存机制

    我们都知道,java程序的跨平台性离不开java虚拟机,虚拟机隔绝了底层操作系统,使得java程序可以直接运行在虚拟机之上.所以,对java的学习,离不开对java虚拟机的学习与了解.下面简单整理下j ...