设计模式——访问者模式(Visitor)

访问者模式可在不修改已有程序结构前提下,定义该类层次结构的操作,通过添加额外的访问者来完成对已有代码功能的提升,满足新增加的需求。
结构对象是使用访问者模式必须条件,且这个结构对象必须存在遍历自身各个对象的方法。类似于java中的collection概念了。
访问器类的开发人员必须清楚将要访问类层次结构的全部或部分设计细节。另在设计访问器类时,我们必须特别注意被访问的对象模型中可能会出现环状结构。
一、组成角色
1、访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。
2、具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作。
3、元素角色(Element):定义一个Accept操作,它以一个访问者为参数。
4、具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。
5、对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。
二、类图

设计模式鈥斺敺梦收吣J(Visitor)
Visitor模式可让不同的访问者对同一个对象集合进行访问;Visitor先调用Visitable类的方法,这个方法又回调到Visitor类。
首先要在原有类层次结构中添加accept方法,然后将这个类层次中的类放到一个对象结构中去,再去创建访问者角色。
三、实现
1、元素接口Visitable
package com.makocn.javapatterns.visitor;
public interface Visitable {
 public void accept(Visitor visitor);
}
2、具体元素类NodeA、NodeB、NodeC
package com.makocn.javapatterns.visitor;
public class NodeA implements Visitable {
 public void accept(Visitor visitor) {
  visitor.visit(this);
 }
}
package com.makocn.javapatterns.visitor;
public class NodeB implements Visitable {
 public void accept(Visitor visitor) {
  visitor.visit(this);
 }
}
package com.makocn.javapatterns.visitor;
public class NodeC implements Visitable {
 public void accept(Visitor visitor) {
  visitor.visit(this);
 }
}
3、访问者接口Visitor
package com.makocn.javapatterns.visitor;
import java.util.Collection;
public interface Visitor {
   public void visit(NodeA nodeA);
   public void visit(NodeB nodeB);
   public void visit(NodeC nodeC);
   public void visitCollection(Collection  collection);
}
4、具体访问者类VisitorA
package com.makocn.javapatterns.visitor;
import java.util.Collection;
import java.util.Iterator;
public class VisitorA implements Visitor {
 public void visit(NodeA a) {
  System.out.println("Execute visitNodeA method!");
 }
 public void visit(NodeB b) {
  System.out.println("Execute visitNodeB method!");
 }
 public void visit(NodeC c) {
  System.out.println("Execute visitNodeC method!");
 }
 public void visitCollection(Collection collection) {
  Iterator iterator = collection.iterator();
  while (iterator.hasNext()) {
   Object o = iterator.next();
   if (o instanceof Visitable)
    ((Visitable) o).accept(this);
  }
 }
}
5、客户端测试类VisitorTest
package com.makocn.javapatterns.visitor;
import java.util.ArrayList;
import java.util.List;
public class VisitorTest {
 public static void main(String[] args) {
  NodeA nodeA = new NodeA();
  NodeB nodeB = new NodeB();
  NodeC nodeC = new NodeC();
  VisitorA visitorA = new VisitorA();
  visitorA.visit(nodeA);
  visitorA.visit(nodeB);
  visitorA.visit(nodeC);
  List<Visitable> list = new ArrayList<Visitable>();
  list.add(nodeA);
  list.add(nodeB);
  list.add(nodeC);
  visitorA.visitCollection(list);
 }
}
对象结构这里没有实现,其元素类直接在客户端直接生成。有兴趣都可以去实现下。
在两个接口Visitor和Visitable中,确保Visitable很少变化,也就是说,确保不能老有新的Element元素类型加进来,可以变化的是访问者行为或操作,也就是Visitor的不同子类可以有多种,这样使用访问者模式最方便。
当然可采用java反射机制, 可以使对象集合中的对象可有变化。
四、优缺点
好处:
给原来类层次增加新操作,不必修改整个类层次,只需实现一个具体访问者角色就可以,这样符合开闭原则的要求。
每个具体的访问者角色都对应一个相关操作,因此如果一个操作需求有变,那么仅修改一个具体访问者角色,而不用改动整个类层次。
由于访问者模式为系统多提供了一层“访问者”,因此可在访问者中添加一些对元素角色的额外操作。
缺点:
开闭原则的遵循总是片面的。如果系统中类层次发生了变化,则必须修改访问者角色和每一个具体访问者角色,所以访问者角色不适合具体元素角色经常发生变化的情况。
访问者角色要执行与元素角色相关操作,就必须让元素角色将内部属性暴露出来,这就意味着其它对象也可访问,这破坏了元素角色的封装性。
在访问者模式中,元素与访问者之间能够传递的信息有限,这往往也会限制访问者模式的使用。
五、适用场景
1、需要对一个对象结构中的对象进行很多不同的且不相关的操作,为了避免让这些操作破坏这些对象的类。Visitor可将相关操作集中起来定义在一个类中。
2、当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
3、对于某对象结构中各元素的操作,如果需要在不修改各元素类的前提下定义作用于这些元素的新操作,也就是动态的增加新的方法可考虑访问者模式。
4、一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。

猜你喜欢

转载自liushilang.iteye.com/blog/1878812