访问者模式看起来是一个非常机智的模式,它做到了将类的架构设计和逻辑代码解耦的目标。为了要完成对访问者模式的解释,我们要明确这两个概念:架构设计和逻辑代码。

架构设计

一般来说我们在设计Java类层次结构的时候,会用到类的继承和扩展。这样一来会产生出一个继承层次,也就是我们的类架构设计。我们在后续的版本更新中,为了满足新的需求,也很可能会发生架构变更的情况。

逻辑代码

针对上述的架构设计,引申出与之相关的业务逻辑和操作。需要说明的是,这些业务操作可能是在架构设计的类中实现的,也可能是在架构设计之外的其他类或其他方法中实现的。

那么为了让架构设计和逻辑代码能够结构,我们就有了访问者模式:

Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

访问者模式代表了一种能够操作在架构设计中的类的方式,它能够让你在不改动类的前提下定义一个新的操作。

还是和之前一样,我们来看一下它的类图:

访问者模式

看起来比较庞大比较唬人,但是我们可以将整个图分为上下两部分:Visitor和Element。

Element

元素,其实就是在整个架构设计中的一个个类结点,它有着自己的业务含义和业务操作。

Visitor

访问者,为了给Element增加新的操作而新建的类,保存了我们想加在Element身上的附加操作。

根据以上的分析,我们可以得出Visitor和Element交互的目的,就是使得Visitor定义的方法能够运行在Element上。为了达到这个效果我们需要进行的就是Visitor和Element的交互,使得某些方法能够被对方访问到。

我们现在来看一下,这两个角色之间实际上是如何进行交互的。首先我们来看一下Visitor应该如何进行定义和实现:

public interface Visitor{
void visit(ConcreteElementA a);
void visit(ConcreteElementB b);
}
public class ConcreteVisitor implements Visitor{
public void visit(ConcreteElementA a){
//do something about a
}
public void visit(ConcreteElementB b){
//do something about b
}
}

从代码我们可以看出,其实是在Visitor的方法中,我们将Element的实例传入了方法,在方法中具体操作这个实例。而Element需要如何与Visitor进行交互呢?

public interface Element{
void accept(Visitor v);
}
public class ConcreteElementA implements Element{
public void accept(Visitor v){
v.visit(this);
}
}
public class ConcreteElementB implements Element{
public void accept(Visitor v){
v.visit(this);
}
}

代码表明其实也是作为参数传入的,而在方法中我们调用了接口方法visit(),并将自己(this)传给了方法,这样得以让visitor访问到了Element,以便在visitor内部进行其他操作。这样一来,为Element新增的方法被放入了一个外部的类中,并且能够灵活增加其他代码,而基本上不需要修改Element的相关代码。我们需要做的,就是在Client堆Element进行使用的时候,传入对应的Visitor类型。