访问者模式是对象的行为模式。
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。
一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
访问者模式的结构
访问者模式涉及的角色:
- 抽象访问者角色(Visitor):声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
- 具体访问者角色(Concrete Visitor): 实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
- 抽象节点角色(Node): 声明一个接受操作,接受一个访问者对象作为一个参量。
- 具体接口(Node): 实现了抽象元素所规定的接受操作。
- 结构对象(ObjectStructure)角色:有如下一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或者集合(Set).
代码清单:
Visitor接口
public interface Visitor { /** * 对应于访问NodeA的访问操作 * @param node */ void visit(NodeA node); /** * 对应于访问NodeB的访问操作 * @param node */ void visit(NodeB node); }
具体访问者VisitorA
public class VisitorA implements Visitor { /** * 对应于NodeA的访问操作 */ @Override public void visit(NodeA nodeA) { System.out.println(nodeA.operationA()); } /** * 对应于NodeB的访问操作 */ @Override public void visit(NodeB nodeB) { // TODO Auto-generated method stub System.out.println(nodeB.operationB()); } }
具体访问者VisitorB
public class VisitorB implements Visitor { /** * 对应于NodeA的访问操作 */ @Override public void visit(NodeA nodeA) { System.out.println(nodeA.operationA()); } /** * 对应于NodeB的访问操作 */ @Override public void visit(NodeB nodeB) { System.out.println(nodeB.operationB()); } }
抽象节点Node
public abstract class Node { /** * 接受操作 * @param visitor */ public abstract void accept(Visitor visitor); }
具体节点NodeA
public class NodeA extends Node { @Override public void accept(Visitor visitor) { visitor.visit(this); } public String operationA(){ return "NodeA is visited"; } }
具体节点NodeB
public class NodeB extends Node { /** * 接受操作 */ @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * NodeB特有的商业方法 * @return */ public String operationB(){ return "NodeB is visited"; } }
结构对象ObjectStructure
public class ObjectStructure { private Vector nodes; private Node node; public ObjectStructure() { nodes=new Vector(); } /** * 执行访问操作 * @param visitor */ public void action(Visitor visitor){ for(Enumeration e = nodes.elements();e.hasMoreElements();) { node = (Node)e.nextElement(); node.accept(visitor); } } /** * 增加一个新的元素 * @param node */ public void add(Node node) { nodes.addElement(node); } }
客户端源码
public class Client { private static ObjectStructure aObjects; private static Visitor visitor; public static void main(String[] args) { // 创建一个结构对象 aObjects = new ObjectStructure(); //给结构增加一个节点 aObjects.add(new NodeA()); //给结构增加一个节点 aObjects.add(new NodeB()); //创建一个新的访问者 visitor = new VisitorA(); //让访问者访问结构 aObjects.action(visitor); } }
在什么情况下应当使用访问者模式
倾斜的可扩展性
访问者模式仅应当在被访问的类结构非常稳定的情况下使用。
也就是说,如果系统的数据结构是频繁变化的,则不适合使用访问者模式。
访问者模式提供了倾斜的可扩展性设计:方法集合的可扩展性和类集合的不可扩展性。
很多系统可以按照算法和数据结构分开,也就是说一些对象含有算法,而另一些对象含有数据,接受算法的操作。
如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,
使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
反过来,如果这样一个系统的数据结构对象易于变化,经常要有新的数据对象增加进来的话,就不适合使用访问者模式。
因为在访问者模式中增加新的节点很困难,要涉及到在抽象访问者和所有具体访问者中增加新的方法。
访问者模式的优点和缺点
优点:
1. 访问者模式使得增加新的操作变的容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,
增加新的操作会变的很复杂。而使用访问者模式,增加新的操作就意味着增加一个新的访问者类,因此,变的很容易。
2. 访问者模式将有关的行为几种到一个访问者对象中,而不是分散到一个个的节点类中。
3.访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。迭代器只能访问属于同一个等级结构的成员对象,
而不能访问属于不同等级结构的对象。
4. 积累状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己的内部,
而不是分散在很多的节点对象中。这是有益于系统维护的优点。
缺点:
1. 增加新的节点类变的困难。每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。
2. 破坏封装。 访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。