访问者(Visitor)模式
# 访问者(Visitor)模式
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
- 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit () ,该操作中的参数类型标识了被访问的具体元素。
- 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
- 抽象元素(Element)角色:声明一个包含接受操作 accept () 的接口,被接受的访问者对象作为 accept () 方法的参数。
- 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept () 操作,其方法体通常都是 visitor.visit (this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
- 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit () ,该操作中的参数类型标识了被访问的具体元素。
/**
* 升级包的接口
*/
public interface Visitor {
void visitDisk(Disk disk);
void visitCpu(CPU cpu);
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
/**
* 升级包可以更改指令
*/
public class UpdatePackage implements Visitor {
private String ext;
public UpdatePackage(String ext) {
this.ext = ext;
}
@Override
public void visitDisk(Disk disk) {
disk.command += "联网存储中..." + ext;
}
@Override
public void visitCpu(CPU cpu) {
// 这里只改指令属性
cpu.command += "联网查询中..." + ext;
// 装饰模式 改方法
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
抽象元素(Element)角色:声明一个包含接受操作 accept () 的接口,被接受的访问者对象作为 accept () 方法的参数。
public abstract class HardWare {
String command; // 封装硬件的处理指令
public HardWare(String command) {
this.command = command;
}
public void work() {
System.out.println("收到指令:" + command);
}
// 定义接受软件升级包的方法,这个方法应该具体硬件去实现
public abstract void accept(Visitor visitor);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept () 操作,其方法体通常都是 visitor.visit (this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
public class CPU extends HardWare {
public CPU(String command) {
super(command);
}
public void work() {
System.out.println("CPU 收到指令:" + command);
}
@Override
public void accept(Visitor visitor) {
// 给升级包提供一个改cpu指令等信息的办法
visitor.visitCpu(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Disk extends HardWare {
public Disk(String command) {
super(command);
}
public void work() {
System.out.println("Disk收到指令:" + command);
}
@Override
public void accept(Visitor visitor) {
// 给升级包提供一个改disk指令等信息的办法
visitor.visitDisk(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
public class XiaoAi {
private CPU cpu = new CPU("武汉天气");
private Disk disk = new Disk("武汉天气");
void answerQuestion() {
cpu.work();
disk.work();
}
// 接受升级包
public void acceptUpdate(Visitor aPackage) {
// 访问模式
// 升级
aPackage.visitCpu(cpu);
aPackage.visitDisk(disk);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
测试
public class MainTest {
public static void main(String[] args) {
XiaoAi xiaoAi = new XiaoAi();
xiaoAi.answerQuestion();
// 升级 cpu联网处理指令 并保存历史数据到云端
UpdatePackage updatePackage = new UpdatePackage("联网增强功能");
xiaoAi.acceptUpdate(updatePackage);
xiaoAi.answerQuestion();
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 应用场景
什么场景用到?
- 在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了 “开闭原则”。
- 违反依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性
- 应用于对象结构相对稳定,但其操作算法经常变化的程序。
- Spring 反射工具中的 MethodVisitor 是什么?
# 注意事项和细节
优点:
- 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
- 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点:
- 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难
- 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
- 因此, 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么 访问者模式就是比较合适的.
编辑 (opens new window)
上次更新: 2023/12/13, 06:06:02