- 简介
1.1 定义
访问者模式(Visitor Pattern)定义:表示一个作用于某对象结构中的各元素的操作,它
使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一
种对象行为型模式。
1.2 结构模式
图1 浏览者模式结构图
1.3 模式分析
想象一个使用场景,有一张药单,里面包含药品、数量、价格等不同类型的集合。对于护士而言看到药品和数量信息就是去取并进行相关医疗工作,而对于缴费工作人员看到药品、数量、价格就是去核对。
这是一个
- 背景:多种类型的物品继承同一个公共祖先,并且在一个集合中。
- 需求:有多种类型的visitor会根据不同的实际类型,需要作出不同决策。
- 访问者模式实例
顾客在超市中将选择的商品,如苹果、图书等放在购物车中,然后到收银员处付款。在购物过程中,顾客需要对这些商品进行访问,以便确认这些商品的质量,之后收银员计算价格时也需要访问购物车内顾客所选择的商品。此时,购物车作为一个 ObjeetStructure(对象结构)用于存储各种类型的商品,而顾客和收银员作为访问这些商品的访问者,他们需要对商品进行检查和计价。不同类型的商品其访问形式也可能不同,如苹果需要过秤之后再计价,而图书不需要。使用访问者模式来设计该购物过程。
图2 购物车类图
1 | java复制代码public abstract class Visitor |
1 | java复制代码public class Customer extends Visitor { |
1 | java复制代码public class Saler extends Visitor { |
1 | java复制代码public interface Product { |
1 | java复制代码public class Apple implements Product { |
1 | java复制代码public class Book implements Product { |
1 | java复制代码import java. util.* |
1 | java复制代码public class Client { |
- 源码分析:Eclipse JDT AST中的访问者模式
在遍历语法树的时候,如果我们需要添加新的访问者,会重写一个新的类去继承 abstract class ASTVisitor
,然后重写我们需要的方法,所有的visit方法默认 return true;。
现在我需要通过语法树去统计每个类被引用的次数,只需要写一个新的类去继承 ASTVisitor 抽象类,然后重写 public boolean visit(MethodDeclaration node)
和 public boolean visit(FieldDeclaration node)
两个函数,统计语法树的子节点出现的被引用的类的情况。
1 | java复制代码@Override |
如何调用呢?使用 compilationUnit.accept(myVisitor);
即可。
我们发现定义新的访问规则其实挺容易的,不用去修改 CompilationUnit
本身,这就是访问者模式的优点。compilationUnit.accept(myVisitor);
的函数执行时,调用的相关源码如下
1 | java复制代码//ASTNode: |
我们发现当调用accep方法时候,实际上是先调用visitor.preVisit(node),然后以前序遍历访问顺序分别访问Package、imports、types(该文件中的其他类),通过函数的重载实现了对于不同类型进行不同visit操作。
类图如图3所示:
图3 ASTNode、ASTVistor源码
总体上写的非常精妙的,但是如果有新的类 MyASTNode 实现了 ASTNode 抽象类,那么需要对ASTVistor 增加 preVisit(MyASTNode node),endVisit(MyASTNode node),visit(MyASTNode node) 三个函数了。不过介于目前Java语言已经趋于稳定,增加新的ASTNode的可能性不大。这里表现了一种 倾斜的开闭原则。
- 总结
- 访问者模式的优点
- 使得增加新的访问操作变得很容易。使用访问者模式,增加新的访问操作就意味着增加一个新的访问者类,无须修改现有类库代码,符合“开闭原则”的要求。
- 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散到一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
- 访问者模式的缺点
- 在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。
- 破坏封裝。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
- 适用场景
- 一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
本文转载自: 掘金