访问者模式
定义
访问者模式(Visitor Pattern)表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
访问者模式通用类图
Visitor ——抽象访问者
抽象类或者接口,声明访问者可以访问哪些元素,具体到程序汇中就是visit方法的参数定义哪些对象是可以被访问的。
ConcreteVisitor——具体访问者
它影响访问者访问到一个类后该怎么干,要做什么事情。
Element——抽象元素
接口或抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。
ConcreteElement——具体元素
实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了。
ObjectStruture——结构对象
元素生产者,一般容纳在多个不同类、不同接口的容器。
访问者模式其实主要就是让接口Element及其实现子类负责数据结构的定义,而关于这些数据的操作,则放在访问者类中,每个访问者子类可以负责实现一系列相关的涉及不同数据子结构(Element子类型)的操作,而不同的访问者子类可以定义完全不相关的操作。也就是说访问者模式的主要作用是把数据结构和作用于结构上的操作解耦合,使得操作集合可以相对自由地演化。这样访问者模式的优点就是增加新的操作容易,因为增加新的操作就意味着增加一个新的访问者;而访问者模式的缺点就是增加新的数据结构比较困难。综上,如果系统有比较稳定的数据结构,又有已于变化的算法的话,使用访问者模式就比较合适。
双分派
与双分派(多分派的一种特殊情况)相对的,是单分派的概念,在选择方法(函数)时,只有一个参量(影响因素)决定具体是哪个方法,比如说多态,虚函数的调用只有在运行时根据对象的具体类型才能决定调用的是哪个函数,多态属于动态单分派,而重载(只有一个参数)则属于静态单分派,根据参数的类型,在编译时决定调用哪个函数。含有多个参数的函数重载,则属于多分派,每个参数都是一个影响因素。即多分派就是影响方法选择的因素有多个,特别的,影响方法选择的因素有两个则称为双分派。
访问者模式是一种双分派的体现,即由Element的具体子类型和Visist的具体子类型决定了Accept操作的不同功能。
举例,来自《大话设计模式》,男人女人。
#include <iostream> #include <string> #include <list> using namespace std; class Man; class Woman; class Action //抽象访问者类 { public: virtual void GetManConclusion(Man *concreteElementA)=0; //针对子元素A的操作 virtual void GetWomanConclusion(Woman *concreteElementB)=0; //针对子元素B的操作 virtual ~Action(){}; }; class Person //抽象元素类 { public: virtual void Accept(Action &visitor)=0; //Accept操作,根据传进去的visitor的具体类型,以及调用其的不同的Person子类型的对象,决定了不同的操作。 }; class Man : public Person //具体元素类 { public: const static string Sex; public: void Accept(Action &visitor) { visitor.GetManConclusion(this); } }; const string Man::Sex("男人"); class Woman : public Person //具体元素类 { public: const static string Sex; public: void Accept(Action &visitor) { visitor.GetWomanConclusion(this); } }; const string Woman::Sex("女人"); class Success : public Action //一个具体的访问者类型 { public: const static string ActionName; public: void GetManConclusion(Man *concreteElementA) { cout<<concreteElementA->Sex<<ActionName<<"时,背后多半有一个伟大的女人。"<<endl; } void GetWomanConclusion(Woman *concreteElementB) { cout<<concreteElementB->Sex<<ActionName<<"时,背后大多有一个不成功的男人。"<<endl; } }; const string Success::ActionName("成功"); class Failing:public Action { public : const static string ActionName; void GetManConclusion(Man *concreteElementA) { cout<<concreteElementA->Sex<<ActionName<<"时,闷头喝酒,谁也不用劝。"<<endl; } void GetWomanConclusion(Woman *concreteElementB) { cout<<concreteElementB->Sex<<ActionName<<"时,眼泪汪汪,谁也劝不了。"<<endl; } }; const string Failing::ActionName("失败"); class Amativeness:public Action { public : const static string ActionName; void GetManConclusion(Man *concreteElementA) { cout<<concreteElementA->Sex<<ActionName<<"时,凡事不懂也要装懂。"<<endl; } void GetWomanConclusion(Woman *concreteElementB) { cout<<concreteElementB->Sex<<ActionName<<"时,遇事懂也装作不懂。"<<endl; } }; const string Amativeness::ActionName("恋爱"); class ObjectStructure { private: list<Person *> elements; public: void Attach(Person *element) { elements.push_back(element); } void Detach(Person *element) { elements.remove(element); } void Display(Action &visitor) { for(list<Person *>::iterator it=elements.begin();it!=elements.end();it++) (*it)->Accept(visitor); } }; int main() { ObjectStructure o; o.Attach(new Man); o.Attach(new Woman); //成功时的反应 Success v1; o.Display(v1); //失败时的反应 Failing v2; o.Display(v2); //恋爱时的反应 Amativeness v3; o.Display(v3); return 0; }
运行结果: