访问者模式是23种设计模式中最复杂的一个,使用频率并不高。大多数情况下,并不需要访问者模式,但一旦需要使用它时,那就真的是需要它了。
1、定义
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下,定义作用于这些元素的新操作。
2、使用场景
- 对象结构比较稳定,但是经常需要在此对象结构上定义新的操作。
- 如果想对一个对象结构中的各个元素进行很多不同的而且不相关的操作,为了避免这些操作使类变得杂乱,可以使用访问者模式,把这些操作分散到不同的访问者中去,每个访问者实现一类功能。
3、UML图
- Visitor:访问者接口。为所有访问者申明访问元素的 visit 方法。
- ConcreteVisitor:具体的访问者实现对象。实现要真正被添加到对象结构中的功能。
- Element:抽象的元素对象。
- ElementA:具体的元素对象,也就是被访问的对象,通常会回调访问者的真实功能,同时开放自身的数据供访问者使用。
- ObjectStructure:对象结构,通常包含多个被访问的对象,让访问者访问他的元素。
4、示例代码
元素的定义:
public abstract class Element {
public abstract void accept(Visitor visitor);
}
public class ElementA extends Element {
//接受访问者的访问
@Override
public void accept(Visitor visitor) {
//调用访问者的访问方接口
visitor.visitElementA(this);
}
//示例方法,表示已有实现
public void operationA() {
}
}
public class ElementB extends Element {
//接受访问者的访问
@Override
public void accept(Visitor visitor) {
//调用访问者的访问方接口
visitor.visitElementB(this);
}
//示例方法,表示已有实现
public void operationB() {
}
}
访问者的定义:
public interface Visitor {
/**
* 访问元素A,相当于给元素A添加访问者的功能
*
* @param element
*/
public abstract void visitElementA(ElementA element);
public abstract void visitElementB(ElementB element);
}
public class ConcreteVisitorA implements Visitor {
@Override
public void visitElementA(ElementA element) {
System.out.println("ConcreteVisitorA 访问 ElementA");
//访问 ElementA 时,把需要实现的操作在这里实现
//可能需要访问元素已有的功能,例如
element.operationA();
}
@Override
public void visitElementB(ElementB element) {
System.out.println("ConcreteVisitorA 访问 ElementB");
//访问 ElementB 时,把需要实现的操作在这里实现
//可能需要访问元素已有的功能,例如
element.operationB();
}
}
public class ConcreteVisitorB implements Visitor {
@Override
public void visitElementA(ElementA element) {
System.out.println("ConcreteVisitorB 访问 ElementB");
//访问 ElementA 时,把需要实现的操作在这里实现
//可能需要访问元素已有的功能,例如
element.operationA();
}
@Override
public void visitElementB(ElementB element) {
System.out.println("ConcreteVisitorB 访问 ElementB");
//访问 ElementB 时,把需要实现的操作在这里实现
//可能需要访问元素已有的功能,例如
element.operationB();
}
}
对象结构的定义:
public class ObjectStructure {
//保存所有需要被访问的元素
private List<Element> elements = new ArrayList<Element>();
public void handleRequest(Visitor visitor) {
//访问所有元素
for(Element ele : elements) {
ele.accept(visitor);
}
}
public void addElement(Element ele) {
elements.add(ele);
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
Element eleA = new ElementA();
Element eleB = new ElementB();
os.addElement(eleA);
os.addElement(eleB);
Visitor visitor = new ConcreteVisitorA();
//让访问者访问对象结构中的元素
os.handleRequest(visitor);
}
}
调用结果:
调用通路
访问者之所以能实现“为一系列对象透明地添加功能”,主要就是依靠通用的方法。访问者说要去访问,就提供一个访问的接口,如visit方法,而对象那边说,好的,我接受你的访问,就提供一个接受访问的接口,如accept方法。这两个方法并不代表任何具体的功能,只是构成一个调用通路。而具体的功能在哪里实现呢?
具体功能的实现在 accept 方法里,通过回调访问者的 visit 方法,从而实现添加新的功能。
基本思想是,软件系统中有一个由许多对象构成的、比较稳定的对象结构,这些对象的类都有一个 accept 接口用来接受访问者的访问,访问者有访问对象的 visit 接口,这个接口对访问到的对象结构中的不同类型的元素会作出不同的处理。在对象结构的一次访问过程中,会遍历整个对象结构,对每一个元素都调用它的 accpt 方法接受访问,在accept 方法里,又会回调访问者的接口 visit ,从而实现访问者可以处理对象结构中的每一个元素。
访问者的本质是预留通路,回调实现。