设计模式-访问者模式 | 8lovelife's life
0%

设计模式-访问者模式

访问者模式是一种行为型设计模式,它可以将相关操作分离并组织在一起,该设计模式允许你在不更改现有代码的情况下很容易就可以添加新的操作,从而提高代码的可维护性和可扩展性。访问者模式是一种强大的设计模式,可以帮助你更好的组织和管理复杂的对象结构。在本篇博客中我们以单据快照为例,来说明访问者模式的特点

简单实现

假设我们的系统中存在多种类型的单据信息,现要求为所有的单据类型创建 HTML 快照

imag

我们可以为系统中每个单据类创建快照方法,但这种方式违反了 SRP 设计原则,并且如果快照的生成方式有所变化,我们需要找到所有不同的单据类,修改其中的快照实现,这是一个很大的工作量

imag

既然这种做法违反了 SRP 原则,那我们尝试把导出快照功能从不同单据类中分离出来

imag

1
2
3
4
5
6
7
8
9
10
11
List<TransRecord> transRecordList = new ArrayList<>();
transRecordList.add(new AfterSaleTransRecord());
transRecordList.add(new InsuranceTransRecord());
transRecordList.add(new MaintainTransRecord());
transRecordList.add(new OrderTransRecord());

TransHtml noramal = new NormalTransHtml();
TransHtml special = new SpecialTransHtml();
for (TransRecord transRecord : transRecordList) {
noramal.snapshot(transRecord); // 编译错误
}

Java 不支持双分派,如代码中的 TransHtml 在运行时会推断出对象的类型为 NormalTransHtml,然后调用具体方法,但是无法通过参数 TransRecord 的类型推导出应该正确调用的方法

双分派:根据涉及的两个对象的运行时类型来决定调用的具体函数

访问者模式

访问者模式中主要有以下角色

  1. Element: 它定义了 accept() 方法,接受一个 Visitor 对象作为参数,以便可以对其访问
  2. ConcreteElement: 实现 Element 接口,并将自己作为参数传递给 Visitor
  3. Visitor: 定义了访问对象结构中元素的新操作方式
  4. ConcreteVisitor: 实现 Visitor 接口,具体操作实现
  5. ObjectStructure: 元素结合,以便访问者可以访问其中的元素

我们通过访问者模式为不同单据类型实现 HTML 快照导出功能

imag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class OrderTransRecord extends TransRecord {
private Long useId;

@Override
public void accept(TransHtml visitor) {
visitor.snapshot(this);
}
}

// ....

List<Accessible<TransHtml>> transRecordList = new ArrayList<>();
transRecordList.add(new AfterSaleTransRecord());
transRecordList.add(new InsuranceTransRecord());
transRecordList.add(new MaintainTransRecord());
transRecordList.add(new OrderTransRecord());

TransHtml noramal = new NormalTransHtml();
// TransHtml special = new SpecialTransHtml();
for (Accessible<TransHtml> accessible : transRecordList) {
accessible.accept(noramal);
}

应用场景

以下是访问者设计模式常见应用场景

  1. GUI框架中,遍历组件层次结构并执行操作,如:计算布局
  2. 游戏中,遍历游戏中的对象并执行操作,如:渲染
  3. 数据库系统中,遍历数据并执行操作,如:聚合