模式动机(Visitor Pattern):访问者模式用于操作存储于某个集合中的各元素,使得可以在不改变元素类的前提下定义作用于这些元素的新操作。
之所以使用访问者类,是因为存储于某个集合中的元素可能具有不同的特性,而不同的访问者可能更看重某一方面的特性,如果让集合类本身承担访问操作,那么对于不同的访问操作,必须对应地定义不同的方法,不仅使得类变得极其庞大,而且难以扩展。
利用访问者模式可以解决上述问题。其将集合中元素的操作封装在一个Visitor继承体系中,不同的Visitor实现类可以满足不同的访问需求,增加新的访问方式只需添加一个ConcreteVisitor,系统中负责存储数据的集合类不需要做丝毫的变动。因此访问者模式的核心就是:在不改变存储元素类的前提下定义作用于这些元素的新操作。
参与者:
Visitor: 定义了访问不同特性元素的接口。
ConcreteVisitor: 实现了对于特定元素的访问操作。
Element: 定义了一个接受访问者的Accept操作接口。
ConcreteElement: 实现了Accept操作,根据不同的元素类型及其访问者类型实现访问操作。
ObjectStructure: 存储不同类型的元素,对外提供增加和删除元素的接口,以及接受一个访问者对象来访问存储的元素。
合作:
一个使用Visitor模式的客户必须创建一个ConcreteVisitor对象,然后遍历存储元素的结构对象ObjectStructure,并用指定的访问者访问这些元素;当一个具体的元素被访问时,它会调用与自身类型相关的Visitor操作。
模式结构图:
模式代码:
bt_访问者模式.h:
1 #ifndef VP_H 2 #define VP_H 3 #include <iostream> 4 #include <list> 5 using namespace std; 6 7 /* 8 抽象元素 9 */ 10 class Visitor; 11 class Element 12 { 13 public: 14 virtual ~Element() = 0; 15 virtual void Accept(Visitor* v) = 0; 16 }; 17 18 /* 19 具体元素 20 */ 21 class ConcreteElementA : public Element 22 { 23 public: 24 void Accept(Visitor* v); 25 void OperationA(); 26 }; 27 class ConcreteElementB : public Element 28 { 29 public: 30 void Accept(Visitor* v); 31 void OperationB(); 32 }; 33 34 /* 35 抽象访问者 36 */ 37 class Visitor 38 { 39 public: 40 virtual ~Visitor() = 0; 41 virtual void VisitConcreteElementA(ConcreteElementA* pEA) = 0; 42 virtual void VisitConcreteElementB(ConcreteElementB* pEB) = 0; 43 }; 44 45 /* 46 具体访问者 47 */ 48 class ConcreteVisitor1 : public Visitor 49 { 50 public: 51 virtual void VisitConcreteElementA(ConcreteElementA* pEA); 52 virtual void VisitConcreteElementB(ConcreteElementB* pEB); 53 }; 54 class ConcreteVisitor2 : public Visitor 55 { 56 public: 57 virtual void VisitConcreteElementA(ConcreteElementA* pEA); 58 virtual void VisitConcreteElementB(ConcreteElementB* pEB); 59 }; 60 61 /* 62 对象结构 63 */ 64 class ObjectStructure 65 { 66 public: 67 ObjectStructure(); 68 ~ObjectStructure(); 69 void AddElement(Element* elem); 70 void RemoveElement(Element* elem); 71 void Accept(Visitor* v); 72 73 private: 74 list<Element*>* elements; 75 }; 76 #endif // VP_H
bt_访问者模式.cpp:
1 #include "bt_访问者模式.h" 2 3 /* 抽象元素 */ 4 Element::~Element(){ } 5 6 /* 具体元素 */ 7 void ConcreteElementA::Accept(Visitor* v){ v->VisitConcreteElementA(this); } 8 void ConcreteElementA::OperationA(){ cout << "Operation A" << endl; } 9 void ConcreteElementB::Accept(Visitor* v){ v->VisitConcreteElementB(this); } 10 void ConcreteElementB::OperationB(){ cout << "Operation B" << endl; } 11 12 13 /* 抽象访问者 */ 14 Visitor::~Visitor(){ } 15 16 /* 具体访问者 */ 17 void ConcreteVisitor1::VisitConcreteElementA(ConcreteElementA* pEA) 18 { 19 cout << "Visitor1 visits ConcreteElementA" << endl; 20 pEA->OperationA(); 21 } 22 void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB* pEB) 23 { 24 cout << "Visitor1 visits ConcreteElementB" << endl; 25 pEB->OperationB(); 26 } 27 void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA* pEA) 28 { 29 cout << "Visitor2 visits ConcreteElementA" << endl; 30 pEA->OperationA(); 31 } 32 void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB* pEB) 33 { 34 cout << "Visitor2 visits ConcreteElementB" << endl; 35 pEB->OperationB(); 36 } 37 38 /* 对象结构 */ 39 ObjectStructure::ObjectStructure(){ elements = new list<Element*>; } 40 ObjectStructure::~ObjectStructure(){ delete elements; } 41 42 void ObjectStructure::AddElement(Element* elem){ elements->push_back(elem); } 43 void ObjectStructure::RemoveElement(Element* elem){ elements->remove(elem); } 44 45 void ObjectStructure::Accept(Visitor* v) 46 { 47 list<Element*>::iterator iter; 48 for(iter = elements->begin(); iter != elements->end(); iter++) 49 { 50 (*iter)->Accept(v); 51 } 52 }
测试用例.cpp:
1 #include "bt_访问者模式.h" 2 3 int main() 4 { 5 cout << "***** 访问者模式测试 *****" << endl; 6 ObjectStructure* obj = new ObjectStructure; 7 Element* elementA1 = new ConcreteElementA; 8 Element* elementA2 = new ConcreteElementA; 9 Element* elementB1 = new ConcreteElementB; 10 Element* elementB2 = new ConcreteElementB; 11 12 obj->AddElement(elementA1); 13 obj->AddElement(elementA2); 14 obj->AddElement(elementB1); 15 obj->AddElement(elementB2); 16 17 Visitor* visitor1 = new ConcreteVisitor1; 18 Visitor* visitor2 = new ConcreteVisitor2; 19 20 obj->Accept(visitor1); 21 cout << endl; 22 obj->Accept(visitor2); 23 24 return 0; 25 }
![](https://images0.cnblogs.com/blog2015/759985/201506/161131377324321.png)
模式分析:
:: 访问者模式使得在不改变元素类的情况下可以增加新的操作,其使用了一种”Double Dispatch”模式,即具体访问操作依赖于具体的Element类型和Visitor类型两个方面,如上程序中:obj->Accept(visitor1) / obj->Accept(visitor2),其中用到了visitor1和visitor2两种访问者类型,而且obj中又存放了两种类型的元素ConcreteElementA / ConcreteElementB。
:: 对象结构的遍历操作由谁负责?
若由对象结构自身负责遍历,则只需要对其包含的集合对象调用Accept()操作即可,我们通常使用这种方式;若由访问者负责遍历,那么意味着每一个具体的ConcreteVisitor都必须负责每一个具体的ConcreteElement的访问,代码复用性太差,难以维护;若由外部迭代器负责遍历,则只能使用元素类型参数,无法使用访问者类型参数,因此无法实现”Double Dispatch”。
:: 如果元素对象本身有多种,而且需要对于每种元素执行不同的操作,那么适合使用Visitor模式,这就需要在设计ConcreteVisitor时,只关注与特定类型元素相关的访问操作即可,没必要关注其他类型元素。当然了,此时抽象的Visitor中必须给出关于其他类型访问的默认实现,而不能再定义为纯虚函数。
:: 如果对象结构类很少改变,但需要经常在该结构上定义新操作,如果此时不使用Visitor模式,那么改变对象结构类时,必须重新定义所有访问者的接口,代价很大。
模式优缺点:
1> Visitor模式使得新增访问操作变得非常容易,只需要增加一个ConcreteVisitor类即可。
2> 与迭代器相比,访问者模式可以访问具有不同类型元素的对象结构;而迭代器只能对同一类型的对象进行操作。
3> 增加新的ConcreteElement类很困难,必须在Visitor接口中添加对应的访问操作,同时,如果Visitor中不给出缺省的实现,那么还必须修改每一个ConcreteVisitor实现类。
4> 访问者模式要求ConcreteElement类提供的功能完全满足访问者的需求,这意味着可能需要提供访问元素内部状态的公共操作,因此会破坏封装性。