意图:
表示一个作用于某对象结构的各元素的操作。它使你可以再不改变各元素的类的前提下定义作用于这些元素的新操作。
动机:
之前在学校的最后一个小项目就是做一个编译器,当时使用的就是访问者模式。
在静态分析阶段,将源程序表示为一个抽象语法树,编译器需要在抽象语法树的基础上实施某些操作以进行静态语义分析。可能需要定义许多操作以进行类型检查、代码优化、流程分析、检查变量是否在使用前被赋值,等等。
这个需求的特点是:要求对不同的节点进行不同的处理。
常规设计方法:不同的节点封装不同的操作。
缺点是,节点类型过多,将操作分散在各个节点类中会导致整个系统难以理解、维护和修改。增加新的操作要修改和重新编译所有的类。
改进:节点类独立于作用于其上的操作。
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; }
运行截图:
参考资料:
《设计模式:可复用面向对象软件的基础》