首页 > 代码库 > 访问者模式

访问者模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述访问者(Visitor)模式的:

访问者模式是对象的行为模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
从定义可以看出结构对象是使用访问者模式的必备条件,而且这个结构对象必须存在遍历自身各个对象的方法

访问者模式结构:

访问者角色(Visitor): 为该对象结构中具体元素角色声明一个访问操作接口. 

具体访问者角色(Concrete Visitor): 实现每个由访问者角色(Visitor)声明的操作.

元素角色(Element): 定义一个Accept操作,它以一个访问者为参数.

具体元素角色(Concrete Element): 实现由元素角色提供的Accept操作.

对象结构角色(Object Structure): 这是使用访问者模式必备的角色. 它要具备以下特征: 能枚举它的元素; 可以提供一个高层的接口以允许该访问者访问它的元素; 可以是一个复合(组合模式)或是一个集合, 如一个列表或一个无序集合.

 

实例1:

public interface Visitor
{
    public void visit(GladiolusConcreteElement gladiolus);

    public void visit(ChrysanthemumConreteElement chrysanthemum);
}

public interface FlowerElement
{
    public void accept(Visitor visitor);
}

public class GladiolusConcreteElement implements FlowerElement
{
    @Override
    public void accept(final Visitor visitor)
    {
        visitor.visit(this);
    }
}

public class ChrysanthemumConreteElement implements FlowerElement
{
    @Override
    public void accept(final Visitor visitor)
    {
        visitor.visit(this);
    }
}

public class GladiolusVisitor implements Visitor
{
    @Override
    public void visit(final GladiolusConcreteElement gladiolus)
    {
        System.out.println(this.getClass().getSimpleName() + " access " + gladiolus.getClass().getSimpleName());
    }

    @Override
    public void visit(final ChrysanthemumConreteElement chrysanthemum)
    {
        System.out.println(this.getClass().getSimpleName() + " access " + chrysanthemum.getClass().getSimpleName());
    }
}

public class ChrysanthemumConreteElement implements FlowerElement
{
    @Override
    public void accept(final Visitor visitor)
    {
        visitor.visit(this);
    }
}

public class ObjectStructure
{
    private final List<FlowerElement> elements = new ArrayList<FlowerElement>();

    public void addElement(final FlowerElement e)
    {
        elements.add(e);
    }

    public void removeElement(final FlowerElement e)
    {
        elements.remove(e);
    }

    public void accept(final Visitor visitor)
    {
        for (final FlowerElement e : elements)
        {
            e.accept(visitor);
        }
    }
}

public class Client
{
    public static void main(final String[] args)
    {
        final ObjectStructure os = new ObjectStructure();
        os.addElement(new GladiolusConcreteElement());
        os.addElement(new ChrysanthemumConreteElement());

        final GladiolusVisitor gVisitor = new GladiolusVisitor();
        final ChrysanthemumVisitor chVisitor = new ChrysanthemumVisitor();

        os.accept(gVisitor);
        os.accept(chVisitor);
    }
}

 运行结果:

GladiolusVisitor access GladiolusConcreteElement
GladiolusVisitor access ChrysanthemumConreteElement
ChrysanthemumVisitor access GladiolusConcreteElement
ChrysanthemumVisitor access ChrysanthemumConreteElement

 

实例2:

interface Service {
    public void accept(Visitor visitor);
}

class Visitor {
    public void process(Service service) {
        // 基本业务
        System.out.println("基本业务");
    }

    public void process(Saving service) {
        // 存款
        System.out.println("存款");
    }

    public void process(Draw service) {
        // 提款
        System.out.println("提款");
    }

    public void process(Fund service) {
// 基金         System.out.println("基金");     } } class Saving implements Service {     public void accept(Visitor visitor) {         visitor.process(this);     } } class Draw implements Service {     public void accept(Visitor visitor) {         visitor.process(this);     } } class Fund implements Service {     public void accept(Visitor visitor) {         visitor.process(this);     } }

 从上面例子可以看出,访问者借助的是java的动态分配机制,使得visitor可以顺利的执行相应对象的方法

//正常逻辑实现
Service service1 = new Saving();
Service service2 = new Fund();
Service service3 = new Draw();
List<Service> ls = new ArrayList<Service>();
ls.add(service1);
ls.add(service2);
ls.add(service3);
for (Service service : ls) {
      if (service instanceof Saving) {
           System.out.println("存款");
      } else if (service instanceof Fund) {
           System.out.println("基金");
      } else if (service instanceof Draw) {
           System.out.println("提款");
      }
}	
            

//上述的问题是随着业务量增大,代码维护量会非常的大,需要不断的去判断 

//采用访问者模式解决
Service saving = new Saving();
Service fund = new Fund();
Service draw = new Draw();
Visitor visitor = new Visitor();
saving.accept(visitor);
fund.accept(visitor);
draw.accept(visitor);

 //上述中accept中实际上有观察者的影子 实际上 访问者我们也可以理解成一个对业务熟悉的观察者

//他不断观察是否有新的业务需求 有的话 进行相应的业务处理

 

小结:
采用Visitor的好处如上面说到的那样,当需要改变其中一项业务的处理时,不需要每个地方都进行修改,而只需要改动Visitor类中相应的处理函数就可以了。也就是说它适合于业务处理时常发生变动的情况。 
当然,Visitor也有它自身的限制。它不适合于业务数量的经常变化,因为一旦新增或删除一些Service时,需要对visitor进行相应的增删。也就是说具体Service与Visitor是耦合的
其实也是将责任抽象成一个个具体的类,从而避免在一个类中既处理逻辑又带有功能,违反了面向对象原则--责任单一


有以下情形可以考虑使用访问者模式:

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

2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。

3、当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。

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

 

 

访问者模式