Design Patterns(十四) Visitor

Visitor design pattern is one of the behavioral design patterns. It is used when we have to perform an operation on a group of similar kind of Objects. With the help of visitor pattern, we can move the operational logic from the objects to another class.

前言

    访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

访问者模式

基本介绍:

1) 访问者模式(Visitor Pattern ),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作;
2) 主要将数据结构与数据操作分离,解决 数 据结构和 操 作耦 合 性问题;
3) 访问者模 式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口;
4) 访问者模 式 主 要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作”污染”这些对象的类,可以选用访问者模式解决。

角色介绍:

Visitor:接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
ElementA、ElementB:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。

访问者模式-UML图:


代码:

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
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

/**
* @Auther: Arsenal
* @Date: 2020-03-19 0:08
* @Description: 访问者模式
*/
public class Vistitor {
public static void main(String[] args) {
// 构建报表
BusinessReport report = new BusinessReport();
System.out.println("=========== CEO看报表 ===========");
report.showReport(new CEOVisitor());
System.out.println("=========== CTO看报表 ===========");
report.showReport(new CTOVisitor());
}
}

/**
* Element角色
*/
abstract class Staff {
public String name;
public int kpi;// 员工KPI

public Staff(String name) {
this.name = name;
kpi = new Random().nextInt(10);
}

// 核心方法,接受Visitor的访问
public abstract void accept(Visitor visitor);
}

/**
* ElementA
* 具体元素类
*/
class Engineer extends Staff {

public Engineer(String name) {
super(name);
}

@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

// 工程师一年的代码数量
public int getCodeLines() {
return new Random().nextInt(10 * 10000);
}
}

/**
* ElementB
* 具体元素类
*/
class Manager extends Staff {

public Manager(String name) {
super(name);
}

@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}

// 一年做的产品数量
public int getProducts() {
return new Random().nextInt(10);
}
}

/**
* Visitor
*/
interface Visitor {
// 访问工程师类型
void visit(Engineer engineer);

// 访问经理类型
void visit(Manager manager);
}

/**
* ObjectStructure
* 员工业务报表类
*/
class BusinessReport {

private List<Staff> mStaffs = new LinkedList<>();

public BusinessReport() {
mStaffs.add(new Manager("经理-A"));
mStaffs.add(new Engineer("工程师-A"));
mStaffs.add(new Engineer("工程师-B"));
mStaffs.add(new Engineer("工程师-C"));
mStaffs.add(new Manager("经理-B"));
mStaffs.add(new Engineer("工程师-D"));
}

/**
* 为访问者展示报表
* @param visitor 公司高层,如CEO、CTO
*/
public void showReport(Visitor visitor) {
for (Staff staff : mStaffs) {
staff.accept(visitor);
}
}
}

// CEO访问者
class CEOVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师: " + engineer.name + ", KPI: " + engineer.kpi);
}

@Override
public void visit(Manager manager) {
System.out.println("经理: " + manager.name + ", KPI: " + manager.kpi +
", 新产品数量: " + manager.getProducts());
}
}

class ReportUtil {
public void visit(Staff staff) {
if (staff instanceof Manager) {
Manager manager = (Manager) staff;
System.out.println("经理: " + manager.name + ", KPI: " + manager.kpi +
", 新产品数量: " + manager.getProducts());
} else if (staff instanceof Engineer) {
Engineer engineer = (Engineer) staff;
System.out.println("工程师: " + engineer.name + ", KPI: " + engineer.kpi);
}
}
}

class CTOVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师: " + engineer.name + ", 代码行数: " + engineer.getCodeLines());
}

@Override
public void visit(Manager manager) {
System.out.println("经理: " + manager.name + ", 产品数量: " + manager.getProducts());
}
}

总结

访问者模式的注意事项和细节:

优点

1) 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高;
2) 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统。

缺点

1) 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难;
2) 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素;
3) 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问
者模式就是比较合适的。

延伸

    访问者模式一篇就够了
    访问者模式-菜鸟教程
    Visitor design pattern
    Design Patterns - Visitor Pattern
    尚硅谷Java设计模式,韩顺平图解java设计模式

Content
  1. 1. 前言
  2. 2. 访问者模式
  3. 3. 总结
  4. 4. 延伸