现实生活中有这样的场景,一个集合对象中存在多种不同的元素,并且每种元素也存在多种不同的访问者和访问方式。如超市中有多种不同的商品,存在多个顾客在买衣服。不同顾客对不同的商品评价也不一样。
当遇到被处理的数据元素相对稳定并且访问方式多种多样的数据结构,我们就可以使用访问者模式。通过访问者模式,我们可以将访问方法从数据结构中分离出来,这样我们可以扩展新的访问方法,不用修改原程序代码就能实现灵活的扩展。
上面的文字你可能云里雾里,现在你只要知道使用访问者模式的作用是将数据结构中的元素与操作方法分离。
“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. (封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。) ”
访问者模式主要由下面5个元素组成:
结构图如下:

访问者模式
public interface Element {
void accept(Visitor visitor);
}
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "具体元素A的操作。";
}
}
public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "具体元素B的操作。";
}
}
public interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
public class ConcreteVisitorA implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("具体访问者A访问-->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具体访问者A访问-->" + element.operationB());
}
}
public class ConcreteVisitorB implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("具体访问者B访问-->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具体访问者B访问-->" + element.operationB());
}
}
public class ObjectStructure {
private List<Element> list = new ArrayList<>();
public void accept(Visitor visitor) {
Iterator<Element> i = list.iterator();
while (i.hasNext()) {
i.next().accept(visitor);
}
}
public void add(Element element) {
list.add(element);
}
public void remove(Element element) {
list.remove(element);
}
}
@Test
public void test() {
ObjectStructure os=new ObjectStructure();
os.add(new ConcreteElementA());
os.add(new ConcreteElementB());
Visitor visitor=new ConcreteVisitorA();
os.accept(visitor);
System.out.println("------------------------");
visitor=new ConcreteVisitorB();
os.accept(visitor);
}
测试结果:
具体访问者A访问-->具体元素A的操作。
具体访问者A访问-->具体元素B的操作。
------------------------
具体访问者B访问-->具体元素A的操作。
具体访问者B访问-->具体元素B的操作。
调用链如下:
//举例A:
ObjectStructure#accept() -> ConcreteElementA#accept() -> ConcreteVisitorA#visit()
那么我们什么时候使用访问者模式呢?
当对象的结构稳定,但操作算法经常会变化的时候,我们为了避免让这些操作算法的变化影响数据结构,就可以使用访问者模式,我们将算法与数据结构分离。
其实我们在使用迭代器遍历集合元素的时候,除了使用迭代器模式,也使用了访问者模式。集合本身不参与遍历,而是通过迭代器的方法去循环获取。
“在这种地方你一定要考虑使用访问者模式:业务规则要求遍历多个不同的对象。这本身也是访问者模式出发点,迭代器模式只能访问同类或同接口的数据(当然了,如果你使用instanceof,那么能访问所有的数据,这没有争论),而访问者模式是对迭代器模式的扩充,可以遍历不同的对象,然后执行不同的操作,也就是针对访问的对象不同,执行不同的操作。 ”
访问者模式是一种集中规整模式,特别适用于大规模重构的项目,在这一个阶段需求已经非常清晰,原系统的功能点也已经明确,通过访问者模式可以很容易把一些功能进行梳理,达到最终目的——功能集中化,如一个统一的报表运算、UI展现等,我们还可以与其他模式混编建立一套自己的过滤器或者拦截器。