Update 设计模式 - 访问者.md

调整意图描述并补充类图中的角色说明;在实现小节补充双分派机制与适用场景说明,并完成代码与文字校对。
This commit is contained in:
CyC2018
2026-10-30 20:19:46 +08:00
parent cbe51085f1
commit da1bf14aa2
+17 -7
View File
@@ -1,16 +1,26 @@
## 访问者(Visitor)
### 意图
为一个对象结构(比如组合结构)增加新能力。
在不修改元素类的前提下,为一个对象结构(比如组合结构)中的元素增加新的操作。
### 类图
- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作;
- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果;
- Element:元素,声明 accept(Visitor) 方法;
- ObjectStructure:对象结构,可以是组合结构,或者是一个集合。
<div align="center"> <img src="pics/dp-visitor.png" width="780px"/> </div><br>
### 实现
下面的例子用一个 GeneralReport 访问者统计客户、订单和商品的数量。元素的 accept() 中调用 visitor.visit(this),这次调用经过两次分派才落到正确的代码上:
- 第一次,customer.accept(visitor) 按元素的实际类型选中 Customer 的 accept();
- 第二次,accept() 中 this 的类型是 Customer,编译时选中 visit(Customer) 这个重载(重载按静态类型选择,见[多态](面向对象思想.md#多态)),运行时再按访问者的实际类型调用 GeneralReport 的实现。
这种做法称为双分派(Double Dispatch)。
新增一种报表只需要再写一个 Visitor 实现,Customer、Order、Item 都不用改;代价是新增一种元素时要修改 Visitor 接口和所有访问者,因此访问者适合元素类型稳定、操作经常增加的场景。
```java
public interface Element {
void accept(Visitor visitor);
@@ -65,7 +75,7 @@ public class Customer implements Element {
public class Order implements Element {
private String name;
private List<Item> items = new ArrayList();
private List<Item> items = new ArrayList<>();
Order(String name) {
this.name = name;
@@ -198,10 +208,10 @@ Number of items: 6
```
### JDK 中的应用
- javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor
- javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor
- javax.lang.model.element.Element 与 javax.lang.model.element.ElementVisitor;
- javax.lang.model.type.TypeMirror 与 javax.lang.model.type.TypeVisitor。
## 参考资料
- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007.
- Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007.
- Gamma E. 设计模式:可复用面向对象软件的基础 [M]. 机械工业出版社, 2007.