22 行为型模式-----访问者模式

模式动机(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 }

 

模式分析:

:: 访问者模式使得在不改变元素类的情况下可以增加新的操作,其使用了一种”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类提供的功能完全满足访问者的需求,这意味着可能需要提供访问元素内部状态的公共操作,因此会破坏封装性。

 

 

 

原文地址:https://www.cnblogs.com/benxintuzi/p/4580312.html