设计模式(22)——访问者模式

本文介绍访问者模式的概念和应用。

基本思想和原则

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新操作。

动机

如果我们要获取很多类的不同对象的信息时,可以考虑使用访问者模式。这可以将访问类对象信息这个操作和类本身分离。

实现

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
164
165
public abstract class Staff {
private String name;
private int age;
private int salary;

public Staff(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return this.age;
}

public void setAge(int age) {
this.age = age;
}

public int getSalary() {
return this.salary;
}

public void setSalary(int salary) {
this.salary = salary;
}

public abstract void accept(IVisitor visitor);
}

public class Empolyee extends Staff {
private String job;

public Empolyee(String name, int age, int salary) {
super(name, age, salary);
}

public String getJob() {
return this.job;
}

public void setJob(String job) {
this.job = job;
}

public void accept(IVisitor visitor) {
visitor.visit(this);
}
}

public class Manager extends Staff {
private int performance;

public Manager(String name, int age, int salary) {
super(name, age, salary);
}

public int getPerformance() {
return this.performance;
}

public void setPerformance(int performance) {
this.performance = performance;
}

public void accept(IVisitor visitor) {
visitor.visit(this);
}
}

public interface IVisitor {
public void visit(Empolyee empolyee);
public void visit(Manager manager);
}

private String info = "";
@Override
public void visit(Empolyee empolyee) {
this.info += this.getBasicInfo(empolyee) + " " + this.getEmpolyeeJob(empolyee) + "\n";
}

@Override
public void visit(Manager manager) {
this.info += this.getBasicInfo(manager) + " " + this.getManagerPerformance(manager) + "\n";
}

private String getBasicInfo(Staff staff) {
return staff.getName() + " " + staff.getAge() + " " + staff.getSalary();
}

private String getEmpolyeeJob(Empolyee empolyee) {
return empolyee.getJob();
}

private int getManagerPerformance(Manager manager) {
return manager.getPerformance();
}

public void showStaffInfo() {
System.out.println(this.info);
}
}

public class BonusVisitor implements IVisitor {
private final static int EMPOLYEE_COEFFICIENT = 2;
private final static int MANAGER_COEFFICIENT = 4;
private int totalBonus = 0;

@Override
public void visit(Empolyee empolyee) {
this.totalBonus += empolyee.getSalary() * EMPOLYEE_COEFFICIENT;
}

@Override
public void visit(Manager manager) {
this.totalBonus += manager.getSalary() * MANAGER_COEFFICIENT;
}

public void showTotalBonus() {
System.out.println("所有人的奖金总和是: " + this.totalBonus);
}
}

public class Test {
public static void main(String[] args) {
InfoVisitor infoVisitor = new InfoVisitor();
BonusVisitor bonusVisitor = new BonusVisitor();
for (Staff staff:getStaffs()) {
staff.accept(infoVisitor);
staff.accept(bonusVisitor);
}

infoVisitor.showStaffInfo();
bonusVisitor.showTotalBonus();
}

private static ArrayList<Staff> getStaffs() {
ArrayList<Staff> staffs = new ArrayList<Staff>();

Empolyee empolyee1 = new Empolyee("Jack", 30, 8000);
Empolyee empolyee2 = new Empolyee("Bob", 23, 4000);
Empolyee empolyee3 = new Empolyee("Jane", 33, 9000);
Manager manager1 = new Manager("John", 50, 20000);

empolyee1.setJob("Sale");
empolyee2.setJob("Engineer");
empolyee3.setJob("Accounting");
manager1.setPerformance(100000);

staffs.add(empolyee1);
staffs.add(empolyee2);
staffs.add(empolyee3);
staffs.add(manager1);

return staffs;
}
}

输出如下:

1
2
3
4
5
6
Jack 30 8000 Sale
Bob 23 4000 Engineer
Jane 33 9000 Accounting
John 50 20000 100000

所有人的奖金总和是: 122000

IVisitor声明了访问者的抽象接口,有两个具体的实现类:InfoVisitorBonusVisitor,分别负责获取雇员的个人信息和计算奖金,这两个类的职责不同,一个打印信息,一个计算奖金,所以分为两个访问者类来实现没什么问题,实际开发中也提倡这么做。

优点

访问者模式符合单一职责原则,被访问者和访问者都可以各自独立发展,互不干扰。扩展性也不错,当需要增加新的访问需求时,只需要增加新的访问者实现类即可。

缺点

访问者模式必须了解一个具体类的细节,者违反了迪米特法则。另外有一种情况的扩展比较困难,当我在具体类中加入新的属性时,就需要修改访问者类,如果访问者类比较多,修改量会很大。访问者模式没有依赖抽象,而是依赖具体,这也违反了依赖倒置原则。