Visitor访问者模式[GoF模式之一]
Visitor设计模式解决的问题是: 当我们需要为一组稳定的继承结构的各类,添加一些方法, 但是我们又不能在这些类里面修改来添加新方法, 也就是为了遵守面向对象设计中的"对扩展开放,对修改关闭"的原则.
下面我们来看一个列子:
就以老师来举例吧, 我们学院有不同的专业, 每个专业的老师上的课也不一样, 比如: 我们可视化专业, 主要是学习.NET知识, 而软件技术专业的主要学习JAVA, 所以每个专业的老师备课就不一样的, 可视化专业的老师备课.NET知识, 软件技术专业的老师备课JAVA知识,这样我们就可以构建一个这样类结构出来:
看上面的类的关系图, Teacher是一个抽象类父类, 下面继承了2个子类, 一个是VisualTeacher,可视化的老师,一个是SoftTeacher,软件技术的老师;
这个两个子类都实现了自己的备课方法,如下代码:
2 {
3 public abstract void PrepareLesson();
4
5 }
6
7 public class VisualTeacher : Teacher
8 {
9 public override void PrepareLesson()
10 {
11 //备课.NET
12 }
13
14 }
15
16
17 public class SoftTeacher : Teacher
18 {
19 public override void PrepareLesson()
20 {
21 //备课JAVA
22 }
23
24 }
现在我们要提出新的需求了, 我现在要为每个专业的老师添加一个上课行为, 只要我们有一点儿面向对象分析的能力的都会想到,这个还不容易,在父类Teacher里面添加一个抽象的方法Teach,然后每个子类重写不就实现了吗,类图关系如下:
代码如下:
2 {
3 public abstract void PrepareLesson();
4
5 public abstract void Teach();
6
7 }
8
9 public class VisualTeacher : Teacher
10 {
11 public override void PrepareLesson()
12 {
13 //备课.Net..
14 }
15
16 public override void Teach()
17 {
18 //教授.Net..
19 }
20
21 }
22
23 public class SoftTeacher : Teacher
24 {
25 public override void PrepareLesson()
26 {
27 //备课JAVA..
28 }
29
30 public override void Teach()
31 {
32 //教授JAVA..
33 }
34
35 }
的确实现了, 但是这样违背的面向对象分析与设计的"对扩展开放,对修改封闭", 我们不可能把已经生成好的assembly修改吧, 所以这种解决办法的方式是不行的!
所以这里我们就要引入GoF模式之一的Visitor访问者模式!
首先我们看看这个在GoF模式教程中的类图结构:
下面我们就带着我们刚才提到的问题进行修改,我们刚才的类图:
代码:
2 {
3 public abstract void Visit(VisiualTeacher visualTeacher);
4
5 public abstract void Visit(SoftTeacher softTeacher);
6
7 }
8
9 public class TeachVisitor : BehaviorVisitor
10 {
11 public override void Visit(VisiualTeacher visualTeacher)
12 {
13 //授课.NET
14 }
15
16 public override void Visit(SoftTeacher softTeacher)
17 {
18 //授课JAVA
19 }
20 }
21
22 public abstract class Teacher
23 {
24 public abstract void PrepareLesson();
25
26 public abstract void Accept(BehaviorVisitor behaviorVisitor);
27
28 }
29
30 public class VisualTeacher : Teacher
31 {
32 public override void PrepareLesson()
33 {
34 //备课.NET
35 }
36
37 public override void Accept(BehaviorVisitor behaviorVisitor)
38 {
39 behaviorVisitor.Visit(this);
40 }
41
42 }
43
44 public class SoftTeacher : Teacher
45 {
46 public override void PrepareLesson()
47 {
48 //备课JAVA
49 }
50
51 public override void Accept(BehaviorVisitor behaviorVisitor)
52 {
53 behaviorVisitor.Visit(this);
54 }
55 }
56
57
58 //客户端使用
59 BehaivorVisitor visitor = new TeachVisitor();
60 Teacher t = new VisualTeacher();
61 t.Accept(visitor); //调用可视化老师的授课方法.
62 //这样就实现了方法的动态添加, 而不用修改原始的类.
看看上面的类图和代码,我们会发现, 在原始的Teacher类,以及它的子类里面多了一个Accept方法,这个方法是最关键的, 他实现动态方法的模板;
然后又多了一个BehaviorVisitor类, 该类定义了动态方法的访问者抽象类, 所有动态方法都从该抽象类进行扩展, 如上面我们扩展了授课的方法的访问者子类(TeachVisitor);
这个BehaviorVisitor抽象类以及它的子类,就在Accept方法中起到了作用.
这样我们就可以 动态的进行扩展更的方法, 再如: 每个专业的老师还会有"开会"的方法, 而可视化专业和软件技术专业的"开会"的方式可能会不一样, 这样我们又可以扩展出会议方法的访问者:MeetingVisitor : BehaviorVisitor. 只要有共同的方法, 而且实现又不相同, 我们就可以进行行为访问者抽象类BehaviorVisitor进行扩展, 这样就遵守了"开放扩展,关闭修改"的原则, 解决了我们最开始提出的问题;
不知道大家注意到没有, 我上面在提出问题的时候, 用红色标出的字体"稳定", 稳定的意思就是说, 继承层次结构是稳定的前提下才能用该设计模式, 例如我们上面提出的例子, Teacher,VisualTeacher,SoftTeahcer, 他们是稳定的, 没有必要再进行扩展出更多的教师类出来, 意思就是只有这么类了. 这个是访问者模式的缺点. 也只有满足了该缺点的情况下才能使用该模式!
还有, 细心的并理解了该设计模式的读者,应该能够发现了, 这里面用到了个多态的地方, 一个是BehaviorVisitor抽象类里面的Visit方法, 一个是Teacher类里面的Accept方法, 这两个方法都父类派生出子类进行多态实现. 这里的多态的行为也叫"多态辨析", 然后在该设计模式中, 这2个方法的多态辨析, 就称为 "双重分发(double dispatch)"
该设计模式的意图:
表示一个作用于某个对象结构中的各元素的操作. 它可以在不改变各个元素的类的前提下定义作用于这些元素的新的操作.
--<<设计模式>>GoF