类层次结构的变化:
类层次结构中可能经常由于引入新的操作,从而将类型变得脆弱...
动机:
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
意图:
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这引起元素的新操作。
结构:
适用性:
1.一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2.需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作"污染"这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
3.定义对象结构的类很少改变,但经常需要在结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
代码实现:
1 // MainApp startup application
2
3 class MainApp
4 {
5 static void Main()
6 {
7 // Setup employee collection
8 Employees e = new Employees();
9 e.Attach(new Clerk());
10 e.Attach(new Director());
11 e.Attach(new President());
12
13 // Employees are 'visited'
14 e.Accept(new IncomeVisitor());
15 e.Accept(new VacationVisitor());
16
17 // Wait for user
18 Console.Read();
19 }
20 }
21
22 // "Visitor"
23
24 interface IVisitor
25 {
26 void Visit(Element element);
27 }
28
29 // "ConcreteVisitor1"
30
31 class IncomeVisitor : IVisitor
32 {
33 public void Visit(Element element)
34 {
35 Employee employee = element as Employee;
36
37 // Provide 10% pay raise
38 employee.Income *= 1.10;
39 Console.WriteLine("{0} {1}'s new income: {2:C}",
40 employee.GetType().Name, employee.Name,
41 employee.Income);
42 }
43 }
44
45 // "ConcreteVisitor2"
46
47 class VacationVisitor : IVisitor
48 {
49 public void Visit(Element element)
50 {
51 Employee employee = element as Employee;
52
53 // Provide 3 extra vacation days
54 Console.WriteLine("{0} {1}'s new vacation days: {2}",
55 employee.GetType().Name, employee.Name,
56 employee.VacationDays);
57 }
58 }
59
60 class Clerk : Employee
61 {
62 // Constructor
63 public Clerk() : base("Hank", 25000.0, 14)
64 {
65 }
66 }
67
68 class Director : Employee
69 {
70 // Constructor
71 public Director() : base("Elly", 35000.0, 16)
72 {
73 }
74 }
75
76 class President : Employee
77 {
78 // Constructor
79 public President() : base("Dick", 45000.0, 21)
80 {
81 }
82 }
83
84 // "Element"
85
86 abstract class Element
87 {
88 public abstract void Accept(IVisitor visitor);
89 }
90
91 // "ConcreteElement"
92
93 class Employee : Element
94 {
95 string name;
96 double income;
97 int vacationDays;
98
99 // Constructor
100 public Employee(string name, double income,
101 int vacationDays)
102 {
103 this.name = name;
104 this.income = income;
105 this.vacationDays = vacationDays;
106 }
107
108 // Properties
109 public string Name
110 {
111 get{ return name; }
112 set{ name = value; }
113 }
114
115 public double Income
116 {
117 get{ return income; }
118 set{ income = value; }
119 }
120
121 public int VacationDays
122 {
123 get{ return vacationDays; }
124 set{ vacationDays = value; }
125 }
126
127 public override void Accept(IVisitor visitor)
128 {
129 visitor.Visit(this);
130 }
131 }
132
133 // "ObjectStructure"
134
135 class Employees
136 {
137 private ArrayList employees = new ArrayList();
138
139 public void Attach(Employee employee)
140 {
141 employees.Add(employee);
142 }
143
144 public void Detach(Employee employee)
145 {
146 employees.Remove(employee);
147 }
148
149 public void Accept(IVisitor visitor)
150 {
151 foreach (Employee e in employees)
152 {
153 e.Accept(visitor);
154 }
155 Console.WriteLine();
156 }
157 }
运行结果:
Visoitr模式的几个要点:
1.Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。
2.所谓双重分发却Visotor模式中间包括了两个多态分发(注意其中的多态机制);第一个为accept方法的多态辨析;第二个为visitor方法的多态辨析。
3.Visotor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visiotr模式适用"Element"类层次结构稳定,而其中的操作却经常面临频繁改动".