定义
表示一个作用于某对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。
应用场景
- 在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
- 如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
结构
代码示例
//Visitor.h
/****************************************************/
#ifndef VISITOR_H
#define VISITOR_H
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>using namespace std;class Visitor;// 抽象元素类-地点(被访问)
class Place
{
public:// 构造函数Place(string name) : m_name(name) {}// 接受访问virtual void accept(Visitor* visitor) = 0;// 获取名字string getName() {return m_name;}private:string m_name;
};// 具体元素类-学校
class School : public Place
{
public:// 构造函数School(string name) : Place(name) {}// 接受访问virtual void accept(Visitor* visitor);};// 具体元素类-企业
class Enterprise : public Place
{
public:// 构造函数Enterprise(string name) : Place(name) {}// 接受访问virtual void accept(Visitor* visitor);};// 抽象访问者
class Visitor
{
public:// 访问学校virtual void visitSchool(School* school) = 0;// 访问企业virtual void visitEnterprise(Enterprise* enterprise) = 0;};// 具体访问者-市长
class Mayor : public Visitor
{
public:// 访问学校virtual void visitSchool(School* school) {cout << "市长参观了:" << school->getName() << endl;cout << "对老师和学生表达了诚挚的慰问。" << endl;}// 访问企业virtual void visitEnterprise(Enterprise* enterprise) {cout << "市长参观了:" << enterprise->getName() << endl;cout << "对企业的发展表示肯定。" << endl;}};// 访问行为类
class Visiting
{
public:// 添加被访问地点void add(Place* place) {places.push_back(place);}// 删除被访问地点void remove(Place* place) {places.erase(std::remove(places.begin(), places.end(), place), places.end());}// 进行访问void accept(Visitor* visitor) {for (auto place : places) {place->accept(visitor);}}private:std::vector<Place*> places;
};// 接受访问
void School::accept(Visitor* visitor) {visitor->visitSchool(this);
}// 接受访问
void Enterprise::accept(Visitor* visitor) {visitor->visitEnterprise(this);
}#endif
//test.cpp
/****************************************************/
#include "Visitor.h"int main()
{Visiting *visiting = new Visiting();Place *school = new School("东华大学");Place *enterprise = new Enterprise("华为");Visitor *mayor = new Mayor();// 添加被访问对象cout << "首日,";visiting->add(school);visiting->add(enterprise);// 安排市长进行访问visiting->accept(mayor);// 次日行程,删除某个被访问对象后再次访问cout << "次日,";visiting->remove(school);visiting->accept(mayor);// 删除delete visiting;delete school;delete enterprise;delete mayor;visiting = nullptr;school = nullptr;enterprise = nullptr;mayor = nullptr;return 0;
}
运行结果
要点总结
- Visitor模式通过所谓双重分发(double dispatch)来实现在不更改(不添加新的操作编译时) Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作(支持变化)。
- 所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制) :第一个为accept方法的多态辨析;第二个为visitElementX方法的多态辨析。
- Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Vistor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动”。