设计模式之访问者模式

访问者模式(Visitor),其含义是表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的前提下定义作用于这些元素的新操作。系统面对新增的需求不能为一个需求的调整而把整个类层次结构全部修改一遍,在原有类层次结构修改也是不错的主意。访问者模式将类结构和对类增加的功能解耦,其目的是要把处理从数据结构中分离出来,从而对客户端的使用代吗进行极大的简化,使得客户使用和编程更加方便。

其适用性:

一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,

需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作”污染“这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作,

定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义所有访问者的接口,这可能需要很多的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

其结构图:

           设计模式之访问者模式

 Visitor访问者定义Visit操作,如 visitConcreteElementA() 和 visitConcreteElementB()操作,指明了要发送visit请求给哪个具体元素类,具体的访问者实现类就可以实现每个由Visitor声明的操作。每个操作实现本算法的一部分,而该算法片段乃是由具体的访问者实现者所完成的。实现如下:

 package org.designpattern.behavioral.visitor;

public abstract class Visitor {
    public abstract void visitConcreteElementA(Element element);
    public abstract  void visitConcreteElementB(Element element);
}

  Element类如下:

package org.designpattern.behavioral.visitor;

public abstract class Element {
    public abstract void accept(Visitor visitor);

} 

  具体的访问者类:

package org.designpattern.behavioral.visitor;

public class ConcreteVisitorA extends 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;

public class ConcreteElementA extends Element {
    @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 class Main {
    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支持动态多分派,就必须执行两次动态的单分派,这种编程方式称为双重分派。 此外,在类聚类变动的情况下不宜使用访问者模式。