diff --git a/notes/设计模式 - 访问者.md b/notes/设计模式 - 访问者.md index 104bd102..3b8f3dfa 100644 --- a/notes/设计模式 - 访问者.md +++ b/notes/设计模式 - 访问者.md @@ -1,16 +1,26 @@ ## 访问者(Visitor) ### 意图 -为一个对象结构(比如组合结构)增加新能力。 +在不修改元素类的前提下,为一个对象结构(比如组合结构)中的元素增加新的操作。 ### 类图 -- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作 -- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果 +- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作; +- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果; +- Element:元素,声明 accept(Visitor) 方法; - ObjectStructure:对象结构,可以是组合结构,或者是一个集合。

### 实现 +下面的例子用一个 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 items = new ArrayList(); + private List 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.