设计模式之访问者模式
其适用性:
一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作”污染“这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作,
定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义所有访问者的接口,这可能需要很多的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
其结构图:
Visitor访问者定义Visit操作,如 visitConcreteElementA() 和 visitConcreteElementB()操作,指明了要发送visit请求给哪个具体元素类,具体的访问者实现类就可以实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是由具体的访问者实现者所完成的。实现如下:
package org.designpattern.behavioral.visitor;
public abstract void visitConcreteElementA(Element element);
public abstract void visitConcreteElementB(Element element);
}
Element类如下:
package org.designpattern.behavioral.visitor;
public abstract void accept(Visitor visitor);
}
具体的访问者类:
package org.designpattern.behavioral.visitor;
@Override
public void visitConcreteElementA(Element element) {
//To change body of implemented methods use File | Settings | File Templates.
System.out.println("visitorA will visit concrete element A!");
}
@Override
public void visitConcreteElementB(Element element) {
//To change body of implemented methods use File | Settings | File Templates.
System.out.println("visitorA will visit concrete element B!");
}
}
具体的Element实现类如下:
package org.designpattern.behavioral.visitor;
@Override
public void accept(Visitor visitor) {
//To change body of implemented methods use File | Settings | File Templates.
visitor.visitConcreteElementA(this);
System.out.println("visit concreteElementA!");
}
}
客户端测试类较为简单:
package org.designpattern.behavioral.visitor;
public static void main(String[] args) {
Visitor visitor = new ConcreteVisitorA();
Element element = new ConcreteElementA();
element.accept(visitor);
}
}
在Visitor模式中Accept()操作是一个双分派的操作。具体调用哪一个具体的Accept()操作,有两个决定因素:1)Element的类型。因为Accept()是多态的操作,需要具体的Element类型的子类才可以决定到底调用哪一个Accept()实现;2)Visitor的类型。Accept()操作有一个参数(Visitor visitor),要决定了实际传进来的Visitor的实际类别才可以决定具体是调用哪个VisitConcrete()实现。Visitor就像我们去商场购物结账时候的收银员,具体的访问者如收银员甲,收银员乙;Element如同我们所要购买的商品货物,具体的Element就是我们的商品A,商品B,而那个objectStructure就像一个购物车一样。
使用访问者模式必须构建至少两个类层次,一个是访问者类层次,另一个是被访问的元素类层次。 在java中支持动态动态单分派和静态多分派,不支持动态多分派。静态分派的参数类型是编程时已经确定的,不支持在运行时刻动态确定其参数类型,所以一定程度上不满足多态的要求,如果想要java支持动态多分派,就必须执行两次动态的单分派,这种编程方式称为双重分派。 此外,在类聚类变动的情况下不宜使用访问者模式。