行为模式---之--访问者模式
所谓双分派,就是根据方法的接收者以及某一个参量的类型的不同而执行不同的代码。Java语言以方法重载的试支持静态的多分派,而通过方法的重写支持动态的单分派
访问者模式是对象的行为模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构之上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。
所涉及角色:
1.抽象访问者(Visitor)角色:声明了一个或多个访问操作,形成所有的具体元素角色必须实现的接口
2.具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
3.抽象节点(Node)角色:声明一个接受操作,接受一个访问者对象作为一个参量
4.具体节点(Node)角色:实现抽象元素所规定的接受操作
5.结构对象(ObjectStructure)角色:有如下责任,可以遍历结构中的所有元素,如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素,如果需要,可以设计成一个复合对象或者一个聚集
示例代码:
1 public class Client { 2 private static ObjectStructure Objects; 3 private static Visitor visitor; 4 public static void main(String[] args) { 5 //创建一个结构对象 6 Objects = new ObjectStructure(); 7 //给结构增加一个节点 8 Objects.add(new NodeA()); 9 //给结构增加一个节点 10 Objects.add(new NodeB()); 11 //创建一个新访问者 12 visitor = new VisitorA(); 13 //让访问者访问结构 14 Objects.action(visitor); 15 } 16 } 17 //抽象访问者 18 interface Visitor{ 19 //对应于nodeA的访问操作 20 void visit(NodeA node); 21 //对应于nodeB的访问操作 22 void visit(NodeB node); 23 24 } 25 //具体访问者A 26 class VisitorA implements Visitor{ 27 28 @Override 29 public void visit(NodeA node) { 30 System.out.println(node.operationA()); 31 } 32 33 @Override 34 public void visit(NodeB node) { 35 System.out.println(node.operationB()); 36 } 37 38 } 39 //具体访问者B 40 class VisitorB implements Visitor{ 41 42 @Override 43 public void visit(NodeA node) { 44 System.out.println(node.operationA()); 45 } 46 47 @Override 48 public void visit(NodeB node) { 49 System.out.println(node.operationB()); 50 } 51 52 } 53 //抽象结点 54 abstract class Node{ 55 //接受操作 56 public abstract void accept(Visitor visitor); 57 } 58 //具体节点A 59 class NodeA extends Node{ 60 61 @Override 62 public void accept(Visitor visitor) { 63 visitor.visit(this); 64 } 65 //nodeA的特有商业方法 66 public String operationA(){ 67 return "Node A is visited"; 68 } 69 } 70 //具体节点B 71 class NodeB extends Node{ 72 @Override 73 public void accept(Visitor visitor) { 74 visitor.visit(this); 75 } 76 //nodeA的特有商业方法 77 public String operationB(){ 78 return "Node B is visited"; 79 } 80 } 81 //结构对象角色,它持有一个聚集,并向外界提供add()方法作为对聚集的管理 操作,通过调用这个方法,可以动态地增加一个新的节点。 82 class ObjectStructure{ 83 private Vector nodes; 84 private Node node; 85 //构造方法 86 public ObjectStructure(){ 87 nodes = new Vector(); 88 } 89 //执行访问操作 90 public void action(Visitor visitor){ 91 for(Iterator it = nodes.iterator();it.hasNext();){ 92 node = (Node)it.next(); 93 node.accept(visitor); 94 } 95 } 96 97 //增加一个新的元素 98 public void add(Node node){ 99 nodes.add(node); 100 } 101 } 102 //此示意性实现中并没有出现一个复杂的具有多个树枝节点的对象树结构,但是,在实际系统中访问者模式通常用来处理复杂的对象树结构的,而且 103 //访问者模式可以用来处理跨越多个等级结构的树结构问题。这正是访问者模式的功能强大之处。
访问者模式不应当在什么情况下使用?
1.倾斜的可扩展性:访问者模式应当在被访问的类结构非常稳定的情况下使用。换另一句话,系统很少出现需要加入新节点的情况。如果出现需要加入新节点的情况,那时就必须在每一个访问对象里加入一个对应于这个新节点的访问操作,而这是对一个系统的大规模修改,是违反“开闭”原则 的
访问者模式允许在节点中加入新的方法,相应的仅仅需要在一个新的访问者类中加入此方法,而不需要在每一个访问者类中都加入此方法。
访问者模式提供了倾斜的可扩展设计,方法集合的扩展性和类集合的不可扩展性。如果系统的数据结构是频繁变化 的,则不适合使用访问者模式
优点
1.访问者模式使得增加新的操作变得容易,如果一个操作依赖于一个复杂的结构对象的话,那么一般而言,增加新的操作会很复杂。而使用访问者模式,增加新的操作就意味着增加一个新的访问者类,很容易
2.访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中
3.访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。迭代子只能访问属于同一个类型等级结构的成员对象,而不能访问属于不同等级结构的对象。访问者模式可以做到这一点
4.积累状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问地过程中将执行操作的状态积累在自己内部,而不是分散到很多的节点对象中。这是有益于系统维护的优点
缺点:
1.增加新的节点类变得很困难。每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。
2.破坏封装。访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。不然,访问者的访问就变得没有意义。由于访问者对象自己会积累访问操作所需要的状态,从而使这些状态不再存储在节点对象中,这也是破坏封装的。