Visitor

访问者

当想要为一个对象的组合增加新的能力,且封装不重要时,就使用访问者模式。可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

适用性

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
  • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

典型情境

客户要求知道每道菜/原料的卡路里/蛋白质情况。此时不需要为每个菜单项和原料都添加这两个方法,只要添加一个访问者,这个访问者可以收集每道菜和原料的信息,有问题客户只要问访问者就可以了。
Visitor Scenario

结构

Visitor Class
Visitor Sequence

Visitor - 为该对象结构中ConcreteElement的每一个类声明一个Visit操作。该操作的名字和特征标识了发送Visit请求给该访问者的那个类。这使得访问者可以确定正被访问的元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
ConcreteVisitor - 实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是对应于结构中对象的类。ConcreteVisitor为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
Element - 定义一个Accept操作,它以一个访问者为参数。
ConcreteElement - 实现Accept操作,该操作以一个访问者为参数。
ObjectStructure - 能枚举它的元素。可以提供一个高层的接口以允许该访问者访问它的元素。可以是一个Composite或是一个集合,如一个列表或一个无序集合。

协作:
一个使用Visitor模式的客户必须创建一个ConcreteVisitor对象,然后遍历该对象结构,并用该访问者访问每一个元素。
当一个元素被访问时,它调用对应于它的类的Visitor操作。如果必要,该元素将自身作为这个操作的一个参数以便该访问者访问它的状态。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include <iostream>
#include <list>
#include <string>
//---------------------------------------------------
class Element;
class ElementA;
class ElementB;
class CompositeElement;
//---------------------------------------------------
class Visitor
{
public:
virtual void visit_elementA(ElementA* ea){}
virtual void visit_elementB(ElementB* eb){}
virtual void visit_composite_element(CompositeElement* ce){}
};
//---------------------------------------------------
class Element
{
public:
virtual ~Element(){}
virtual void accept(Visitor& v) = 0;
};
//---------------------------------------------------
class ElementA : public Element
{
public:
ElementA() : Element(), m_a_num(50), m_a_string("I'm A. "){}
virtual void accept(Visitor& v)
{

v.visit_elementA(this);
}
int get_a_num() {return m_a_num;}
std::string get_a_string() {return m_a_string;}
protected:
int m_a_num;
std::string m_a_string;
};
//---------------------------------------------------
class ElementB : public Element
{
public:
ElementB() : Element(), m_b_num(30), m_b_string("I'm B. "){}
virtual void accept(Visitor& v)
{

v.visit_elementB(this);
}
int get_b_num() {return m_b_num;}
std::string get_b_string() {return m_b_string;}
protected:
int m_b_num;
std::string m_b_string;
};
//---------------------------------------------------
class CompositeElement : public Element
{
public:
void add(Element* e) {m_children.push_back(e);}
void remove(Element* e) {m_children.remove(e);}
virtual void accept(Visitor& v)
{

for (std::list<Element*>::iterator it = m_children.begin(); it != m_children.end(); ++it)
{
if (!(*it)) continue;
(*it)->accept(v);
}
v.visit_composite_element(this);
}
int get_composite_num()
{

return m_children.size();
}
std::string get_composite_string()
{

return "From All of Us. ";
}
protected:
std::list<Element*> m_children;
};
//---------------------------------------------------
class ConcreteVisitor1 : public Visitor
{
public:
ConcreteVisitor1() : Visitor(), m_total(""){}
std::string get_total_string() {return m_total;}
virtual void visit_elementA(ElementA* ea)
{

if (!ea) return;
m_total += ea->get_a_string();
}
virtual void visit_elementB(ElementB* eb)
{

if (!eb) return;
m_total += eb->get_b_string();
}
virtual void visit_composite_element(CompositeElement* ce)
{

if (!ce) return;
m_total += ce->get_composite_string();
}
protected:
std::string m_total;
};
//---------------------------------------------------
class ConcreteVisitor2 : public Visitor
{
public:
ConcreteVisitor2() : Visitor(), m_total(0) {}
int get_total_num() {return m_total;}
virtual void visit_elementA(ElementA* ea)
{

if (!ea) return;
m_total += ea->get_a_num();
}
virtual void visit_elementB(ElementB* eb)
{

if (!eb) return;
m_total += eb->get_b_num();
}
virtual void visit_composite_element(CompositeElement* ce)
{

if (!ce) return;
m_total += ce->get_composite_num();
}
protected:
int m_total;
};
//---------------------------------------------------
class ObjectStructure
{
public:
void test2()
{

Element* ea = new ElementA();
Element* eb = new ElementB();
ConcreteVisitor2 cv2;
ea->accept(cv2);
eb->accept(cv2);
std::cout << "Get Int Sum of ElementA and ElementB: (50 + 30 = 80)" << std::endl;
std::cout << cv2.get_total_num() << std::endl;
}
void test1()
{

Element* ea = new ElementA();
Element* eb = new ElementB();
CompositeElement* ce = new CompositeElement();
ce->add(ea);
ce->add(eb);
ConcreteVisitor1 cv1;
ce->accept(cv1);
std::cout << "Get String Sum of CompositeElement: (ElementA + ElementB + CompositeElement)" << std::endl;
std::cout << cv1.get_total_string() << std::endl;
}
};
//---------------------------------------------------
int main()
{

ObjectStructure o;
o.test1();
std::cout << std::endl;
o.test2();
system("Pause");
}

优点

  • 允许你对组合结构加入新的操作,而无需改变结构本身。
  • 想要加入新的操作,相对容易。
  • 访问者所进行的操作,其代码是集中在一起的。

用途和缺点

  • 当采用访问者模式的时候,会打破组合类的封装。
  • 因为游走的功能牵涉其中,所以对组合结构的改变就更加困难。

相关模式

  • Composite: 访问者可以用于对一个由Composite模式定义的对象结构进行操作。
  • Interpreter: 访问者可以用于解释。

[1] 设计模式:可复用面向对象软件的基础
[2] Head First设计模式