设计模式之访问者
定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。
设想一个场景,就是学校,学校里有老师和学生,老师和学生可以称为两种元素,我们可以对这些元素进行很多操作(注意,这些操作都是外部性质的,不属于元素本身,这个至关重要),比如评价,问卷调查,采访和体检等,如果我们把这些操作定义在元素层次,显然不合理,一是这些操作不是元素对象的固有属性和行为,而是一些外部操作,不符合面向对象的封装和责任单一原则。如果把这些操作定义在学校这层,显然也不合理,因为学校的责任只是对老师和学生进行管理,这些操作也不是学校的职责。既然不能定义在元素层和学校层,我们可以单独定义一个层次结构来实现,但这里面有两个问题,一是这些操作可能对不同的元素有不同的形式,二是这些操作还必须得到元素的许可,即元素可以接收你的访问。上面的场景其实就是访问者模式的典型应用场景
适用情况 :
1) 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2) 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。
对于系统中一个已经完成的类层次结构,我们已经给它提供了满足需求的接口。但是面对新增加的需求,我们应该怎么做呢?如果这是为数不多的几次变动,而且你不用为了一个需求的调整而将整个类层次结构统统地修改一遍,那么直接在原有类层次结构上修改也许是个不错的主意。
但是往往我们遇到的却是:这样的需求变动也许会不停的发生;更重要的是需求的任何变动可能都要让你将整个类层次结构修改个底朝天。这种类似相同的操作分布在不同的类里面,不是一个好现象,我们要对这个结构重构一下了。
那么,访问者模式也许是你很好的选择(定义一个公共的接口,然后实现它并重构需求变动)
以下是访问者模式的组成结构:
1) 访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口(客户端和服务器端通信的桥梁,也就是通过接口访问)。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
2) 具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作(客户端)。
3) 元素角色(Element):定义一个Accept操作,它以一个访问者为参数(客户端需要接受的方法)。
4) 具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。
5) 对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:
(1)能枚举它的元素(注册的服务个数);可以提供一个高层的接口以允许该访问者访问它的元素(接口);可以是一 个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
实现思路:向对象结构类中添加元素,然后定义多种访问者,使用对象结构类去接受访问者即可。
类图:

应用场景:用户去访问博客
分析:用户可以通过电脑上的Web方式(访问者)或者手机Wap方式(访问者)去访问博客,每篇博客是一个元素,然后博客列表是一个对象结构类。
--------------------------
【2】访问者模式代码示例:
代码示例1(一般写法):
1 #include <iostream>
2 #include <list>
3 #include <string>
4 using namespace std;
5
6 class Person
7 {
8 public:
9 string action;
10 virtual void getConclusion() = 0;
11 };
12
13 class Man : public Person
14 {
15 public:
16 void getConclusion()
17 {
18 if (action == "成功")
19 {
20 cout << "男人成功时,背后多半有一个伟大的女人。" << endl;
21 }
22 else if (action == "恋爱")
23 {
24 cout << "男人恋爱时,凡事不懂装懂。" << endl;
25 }
26 }
27 };
28
29 class Woman : public Person
30 {
31 public:
32 void getConclusion()
33 {
34 if (action == "成功")
35 {
36 cout << "女人成功时,背后多半有失败的男人。" << endl;
37 }
38 else if (action == "恋爱")
39 {
40 cout << "女人恋爱时,遇到事懂也装不懂。" << endl;
41 }
42 }
43 };
44
45 void main()
46 {
47 list<Person*> persons;
48
49 Person *pMan1 = new Man();
50 pMan1->action = "成功";
51 persons.push_back(pMan1);
52
53 Person *pWoman1 = new Woman();
54 pWoman1->action = "成功";
55 persons.push_back(pWoman1);
56
57 Person *pMan2 = new Man();
58 pMan2->action = "恋爱";
59 persons.push_back(pMan2);
60
61 Person *pWoman2 = new Woman();
62 pWoman2->action = "恋爱";
63 persons.push_back(pWoman2);
64
65 list<Person*>::iterator iter = persons.begin();
66 while (iter != persons.end())
67 {
68 (*iter++)->getConclusion();
69 }
70 }
71 //Result:
72 /*
73 男人成功时,背后多半有一个伟大的女人。
74 女人成功时,背后多半有失败的男人。
75 男人恋爱时,凡事不懂装懂。
76 女人恋爱时,遇到事懂也装不懂。
77 */
代码示例2(访问者模式写法):
1 #include <iostream>
2 #include <list>
3 #include <string>
4 using namespace std;
5
6 class Man;
7 class Woman;
8
9 class Action
10 {
11 public:
12 virtual void getManConclusion(Man *) = 0;
13 virtual void getWomanConclusion(Woman *) = 0;
14 };
15
16 class Success : public Action
17 {
18 void getManConclusion(Man *)
19 {
20 cout << "男人成功时,背后多半有一个伟大的女人" << endl;
21 }
22 void getWomanConclusion(Woman *)
23 {
24 cout << "女人成功时,背后多半有不成功的男人" << endl;
25 }
26
27 };
28
29 class Love : public Action
30 {
31 void getManConclusion(Man *)
32 {
33 cout << "男人恋爱时,凡事不懂装懂" << endl;
34 }
35 void getWomanConclusion(Woman *)
36 {
37 cout << "女人恋爱时,凡事懂也装不懂" << endl;
38 }
39 };
40
41 class Person
42 {
43 public:
44 virtual void accept(Action*) = 0;
45 };
46
47 class Man : public Person
48 {
49 public:
50 void accept(Action* visitor)
51 {
52 visitor->getManConclusion(this);
53 }
54 };
55
56 class Woman : public Person
57 {
58 public:
59 void accept(Action* visitor)
60 {
61 visitor->getWomanConclusion(this);
62 }
63 };
64
65 class ObjectStructure
66 {
67 private:
68 list<Person*> elements;
69
70 public:
71 void attach(Person* element)
72 {
73 elements.push_back(element);
74 }
75 void detach(Person* element)
76 {
77 for (list<Person*>::iterator iter = elements.begin(); iter != elements.end(); ++iter)
78 {
79 if ((*iter) == element)
80 {
81 elements.erase(iter);
82 }
83 }
84 }
85 void display(Action *visitor)
86 {
87 for (list<Person*>::iterator iter = elements.begin(); iter != elements.end(); ++iter)
88 {
89 (*iter)->accept(visitor);
90 }
91 }
92 };
93
94 int main()
95 {
96 ObjectStructure objStructure;
97 Success *os = new Success();
98 Love *lv = new Love();
99
100 Person *man = new Man();
101 objStructure.attach(man);
102
103 Person *woman = new Woman();
104 objStructure.attach(woman);
105
106 objStructure.display(os);
107 objStructure.display(lv);
108
109 return 0;
110 }
111 //Result:
112 /*
113 男人成功时,背后多半有一个伟大的女人
114 女人成功时,背后多半有不成功的男人
115 男人恋爱时,凡事不懂装懂
116 女人恋爱时,凡事懂也装不懂
117 */
【3】访问者模式角色分析
在访问者模式中,主要包括下面几个角色:
抽象访问者:一般为抽象类或者接口,声明访问者可以访问哪类元素。
具体到程序中就是visit方法中的参数定义哪些类型的元素是可以被访问的。
访问者:实现抽象访问者所声明的方法,它影响到访问者具体访问到一种元素后该干什么,要做什么事情。
抽象元素类:一般为抽象类或者接口,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。
抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
元素类:实现抽象元素类所声明的accept方法,通常都是以this指针为实参,基本上已经形成一种定式。
结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。
【4】访问者模式总结
应用情景:访问者模式适用于数据结构稳定的系统。它实质上把数据结构和作用于数据结构上的操作分离开,使得操作集合。
因为访问者模式使得算法操作增加变得更容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。
优点:新增加操作很容易,因为新增加操作相当于增加一个访问者。
由以上示例可以得知:访问者模式将有关的行为集中到一个访问者对象中。
比如,我们现在需求再增加一个“失败”的操作。只需要继承自Action再实现一个所谓的 “失败” 访问者。
参考:http://www.cnblogs.com/Braveliu/p/3957007.html
http://blog.csdn.net/hawksoft/article/details/6639786
http://www.cnblogs.com/chengxingliang/archive/2011/10/24/2214033.html
http://cqfly.iteye.com/blog/975791
http://blog.csdn.net/m13666368773/article/details/7711326

浙公网安备 33010602011771号