访问者模式
模式定义
访问者模式表示一个作用于某对象结构中各元素的操作。 它允许在不改变各元素的类的前提下定义作用于这些元素的新操作。在GOF95中,该模式被分类为行为型模式。
模式结构与说明
- Visitor:访问者接口,为所有元素声明一个visit方法,用来代表为此元素添加的功能
- ConcreteVisitor:具体访问者实现,实现一组完整的应用到对象结构的功能
- Element:抽象元素,代表对象结构中具体的元素,定义接收访问的操作
- ConcreteElement:具体元素,亦即被访问对象,通常会回调访问者的真实功能,同时开放自身的数据供访问者使用
- ObjectStructure:对象结构,通常包含多个可被访问的元素,它可以遍历多个被访问的元素,也可以让访问者直接访问元素。该对象可以持有若干Element的集合,或者具有多个Element类型的字段
访问者模式的价值在于可以透明的为对象结构添加新功能,避免对这一系列的对象进行修改。
访问者模式的优点:
- 更好的可扩展性:允许通过创建新的访问者来增加行为,而无需改变对象结构本身
- 行为的代码被放在一起,统一位于访问者内,易于维护
- 分离无关行为:相互之间无关的行为被封装到不同的访问者中
访问者模式的缺点:
- 允许访问者对这些内部元素进行访问,对象结构的内部封装被打破
- 由于访问者对结构进行遍历,对象结构的修改变得困难,因为牵涉到所有访问者的修改
访问模式的适用场景:
- 如果需要对一个对象结构实施一些依赖于具体元素的操作
- 如果需要对一个对象结构中的各元素实施很多不同、不相关的操作
- 如果对象结构很少变动,但是经常需要对元素添加新功能
经典应用
java.lang.model包中的访问者模式
java.nio.file包中的访问者模式
这是一个变形的访问者模式,元素:文件、目录等没有统一的接口,也不能accept()访问者,实质上,对访问者的回调是由FileTreeWalker.walk()方法手动判断和完成的。FileTreeWalker相当于ObjectStructure角色。
ASM框架
ASM是Java的一个字节码处理框架,它定义的访问者模式允许访问类中的各种元素:类本身、方法、字段、注解等:
Dom4j中的访问者模式
Dom4j是一个XML解析框架,它实现了非常典型的访问者模式:Visitor接口定义了访问各种XML节点的操作,包括文档、元素、属性、文本等。Node接口相当于Element角色,它为所有类型的XML节点定义了accept()方法。Document接口的实现AbstractDocument则充当了ObjectStructure角色:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public abstract class AbstractDocument extends AbstractBranch implements Document { public void accept(Visitor visitor) { visitor.visit(this); //先让访问者访问自己 DocumentType docType = getDocType(); if (docType != null) { visitor.visit(docType); //访问文档类型 } List content = content(); //遍历,访问所有子节点 if (content != null) { for (Iterator iter = content.iterator(); iter.hasNext();) { Object object = iter.next(); if (object instanceof String) { Text text = getDocumentFactory() .createText((String) object); visitor.visit(text); //访问文本 } else { //如果是节点,那么调用相应的accept,递归的处理整个XML文档 Node node = (Node) object; node.accept(visitor); } } } } } |
Dom4j的访问者模式类图如下:
模式演变
- 与缺省适配模式联用:如果元素种类特别多,则应当引入一个实现了缺省适配的访问者抽象类,提供缺省适配,这样可以减少具体访问者的代码量
- 与组合模式联用:将Component中定义的业务方法抽取出来,改用accept()方法,然后将那些业务方法的逻辑转移到访问者中
- 退化:取消Element接口。这种情况下,各元素没有一致的接口,不能accept()访问者。那么把哪些元素传递给访问者对应方法的职责就落到ObjectStructure头上了
Leave a Reply