访问者模式是对象的行为模式。访问者模式的目的是封装施加在某种数据结构元素上的操作。一旦一些操作需要修改,接受这个操作的数据结构可以保持不变。
个人觉得访问者模式相对其他的设计模式来说稍微复杂,难理解一点,要理解这个模式首先需要了解“单分派与多分派”。
单分派与多分派
根据对象的类型对执行方法进行选择,就是分派(Dispatch)。分派是面向对象语言提供的关键特性之一,根据分派发生的时期,可分为两种,静态分派和动态分派。静态分派发生在编译时期,动态分派发生在执行时期。
重载方法的分派是根据静态类型进行的,分派过程发生在编译时期,属于静态分派。
Java通过Override重写来支持动态分派,Java编译器并不会知道执行阶段会执行哪段重写的方法。
Java语言支持静态的多分派和动态的单分派,分别是通过重载方法和重写方式实现的。
Java语言通过两个宗量来决定执行哪一段代码:
方法的接受者:也即方法执行的对象,方法的接受者,动态分派根据具体类型决定;
事件对象:一个类行为的对象,传输的参数类型。
而访问者模式就是通过重写和重载的结合使用来达到实现双重分派的目的。
访问者模式
下面结合我们实际项目中的需求来阐述一下访问者模式。
访问者模式的类图如下:
所涉及到的角色:
抽象访问者:声明一个或多个访问操作,形成所有的具体元素角色必须实现的接口。下面的示例就是资源访问操作的顶层接口。
public interface IResourceVisitor {
//detail resource
void visit(JavaClassResource resource) throws ResourceParserException;
void visit(MetadataResource resource) throws ResourceParserException;
void visit(UpmResource resource) throws ResourceParserException;
void visit(XmlResource resource) throws ResourceParserException;
void visit(PropertiesResource propertiesResource) throws ResourceParserException;
//composite resource
void visit(BusinessComponentResource resource) throws ResourceParserException;
void visit(JavaClassSrcResource resource) throws ResourceParserException;
具体访问者:实现抽象访问者角色所声明的接口,就是抽象访问所声明的各个访问操作。一般情况下,由于一般情况下抽象访问者的方法都比较多,而且具体访问者也不会关心抽象访问者的所有方法,因此通常有一个默认的实现Adaptor介于两者之间。
Adaptor也是一个非常重要的类,对于Composite类型的资源,存在一个默认遍历策略。
public abstract class AbstractResourceVisitorAdaptor implements IResourceVisitor {
@Override
public void visit(JavaClassResource resource) throws ResourceParserException {
}
@Override
public void visit(MetadataResource resource) throws ResourceParserException {
}
@Override
public void visit(UpmResource resource) throws ResourceParserException {
}
@Override
public void visit(XmlResource resource) throws ResourceParserException {
}
@Override
public void visit(PropertiesResource propertiesResource) throws ResourceParserException {
}
@Override
public final void visit(BusinessComponentResource resource) throws ResourceParserException {
visitComposite(resource);
}
protected final void visitComposite(CompositeResource compositeResource) throws ResourceParserException {
List<IResource> subResources = compositeResource.getSubResources();
for (IResource subResource : subResources) {
subResource.accept(this);
}
}
}
抽象节点:声明一个接受操作,接受一个访问者对象作为一个参量。
public interface IResource {
/**
* Visitor mode to access resources.
*
* @param resourceVisitor
*/
void accept(IResourceVisitor resourceVisitor) throws ResourceParserException;
}
具体节点:实现抽象元素规定的accept操作,通常这个接口的层次结构反映了整个程序的数据结构。
结构对象: 结构对象可以遍历结构中的所有元素,如果需要,提供一个高层次的接口让访问者对象能够访问每一个元素;如果需要,可以设计成复合对象或一个聚集。
最终的UML类图结构如下所示(已隐藏过多的实现):
使用场景
访问者模式提供了一种倾斜的可扩展性,仅在当被访问的类结构非常稳定的时候使用(就比如PMD的类库中对Java代码结构进行了分析,Java类的结构语法相对来说非常稳定,其使用的就是访问者模式)。系统应该很少出现需要加入新节点的情况。其倾斜的可扩展性体现在:方法集合的可扩展性和类集合的不可扩展性。