访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义于作用于这些元素的新操作。
分派
参考《深入理解 Java
虚拟机:JVM
高级特性与最佳实践 第 2 版》 第 8.3.2 章:分派,这一章中介绍了分派的概念,以及静态分派和动态分派的含义,给出的结论是 Java
是一门静态多分派和动态单分派的语言。
具体分析也可以参考:Java 面向对象的特征 – 方法调用:分派
访问者模式
访问者模式 Visitor Pattern
:把数据处理和数据结构分离。在数据结构相对稳定时,访问者模式使得数据处理的算法修改或增加都变得非常容易。
访问者模式使用两次动态单分派来间接实现伪动态双分派,具体可以参考类图及代码分析。
类图结构
结构解析
Element
抽象类,元素,定义一个accept
方法,表示每个元素都能被访问者访问。Visitor
抽象类,访问者,定义对每一个元素访问的行为,它的参数就是可以访问的元素。虚方法个数理论上和元素子类个数是一致的,表示访问每一个元素。ConcreteElement
实现类,元素中accept
的具体实现。实现格式很固定,就是调用Visitor
中访问该具体元素的方法。充分利用双分派技术,实现数据处理和数据结构的分离。ConcreteVisitor
实现类,定义访问者访问某个元素时具体的行为。ObjectStructure
定义一个数据结构对象,管理元素的集合,并能够让访问者遍历访问每个元素。Client
客户端。
访问者模式中 ObjectStructure, Element, ConcreteElement
三者很像一个观察者模式。访问者模式的优势在与数据处理和数据结构的分离,其中数据结构即为 Element
,数据处理即为 Visitor
。修改数据的处理方式非常简单,但是如果增加数据结构将会变得很麻烦。
示例
1 | // 1. Element |
示例中可以看到,每次接收一个访问者,该访问者都会遍历所有的元素。
访问者模式和双分派
双分派体现在 accept
这个方法上:
- 第一次动态分派
在ObjectStructure.accept
中遍历执行Element.accept
时,并不确定接受者具体是哪个ConcreteVisitor1/ConcreteVisitor2
,运行时确定。 - 第二次动态分派
在Element.accept
中,Visitor
并不确定参数具体是哪个ConcreteElementA/ConcreteElementB
,运行时确定。
组合实现了双分派,只有在运行时才能确定是哪个 Visitor
访问哪个 Element
。
总结
访问者模式的几个特点:
- 把数据结构(
Element
)和作用于结构上的操作(Visitor
)解耦合,使得操作集合可相对自由地演化 - 适用于数据结构相对稳定算法又易变化的系统,算法操作增加很容易
- 优点是增加操作很容易,其缺点就是增加新的元素会非常困难