设计模式-访问者模式
访问者者模式的英文翻译是 Visitor Design Pattern。它是这么定义的:Allows for one or more operation to be applied to a set of objects at runtime, decoupling the operations from the object structure. 翻译成中文就是:允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。
案例分析
如果有一个接口,这个接口有三个实现类,那么我们可以充分利用多态特性进行处理
java
public interface Visit {
void visit();
}
public class AVisitor implements Visit {
@Override
public void visit() {
System.out.println("AVisitor:visit");
}
}
public class BVisitor implements Visit {
@Override
public void visit() {
System.out.println("BVisitor:visit");
}
}
public class CVisitor implements Visit {
@Override
public void visit() {
System.out.println("CVisitor:visit");
}
}
由于这三个实现类都实现了该接口,因此可以借助多态特性使用
java
public class Main {
public static void main(String[] args) {
List<String> types = List.of("A","B","C");
String type = types.get(new Random().nextInt(3));
Visit visit = new AVisitor();
if (type.equals("A")) {
visit = new AVisitor();
}
if (type.equals("B")) {
visit = new BVisitor();
}
if (type.equals("C")) {
visit = new CVisitor();
}
processVisit(visit);
}
private static void processVisit(Visit visit) {
visit.visit();
}
}
这也是我们经常用到的方式
但是随着接口的方法增加,所有的实现类都需要新增实现接口的内容,如果接口的内容过多,会导致实现类的内容非常庞大,那么我们可以反向思考,按照功能实现接口,还是如上的场景,例如 ABC 三个实现类都需要实现接口中的 funcation1 和 funcation2 方法,我们定义如下接口
java
public interface Visit {
void visit(AFuncationImpl a);
void visit(BFuncationImpl b);
void visit(CFuncationImpl c);
}
利用接口重载支持多个实现类,接着我们按照功能拆分为 funcation1 类 和 funcation2 类
java
public class Funcation1Visitor implements Visit {
@Override
public void visit(AFuncationImpl a) {
System.out.println("AVisitor Funcation1");
}
@Override
public void visit(BFuncationImpl b) {
System.out.println("BVisitor Funcation1");
}
@Override
public void visit(CFuncationImpl c) {
System.out.println("CVisitor Funcation1");
}
}
public class Funcation2Visitor implements Visit {
@Override
public void visit(AFuncationImpl a) {
System.out.println("AVisitor Funcation2");
}
@Override
public void visit(BFuncationImpl b) {
System.out.println("BVisitor Funcation2");
}
@Override
public void visit(CFuncationImpl c) {
System.out.println("CVisitor Funcation2");
}
}
经过这样的拆分,可以看出每个 Funcation 中一般只有实现类个方法。
那么该如何使用呢,每一个 FuncationVisitor 的参数已经变成具体的实现类类,是不是就无法充分利用多态特性了?
我们可以再定义一个接口
java
public interface Accept {
void accept(Visit visit);
}
所有的 CFuncationImpl 实现这个接口,并传入具体的 FuncationVisitor,这样就能充分利用多态特性了
java
public class AFuncationImpl implements Accept {
@Override
public void accept(Visit visit) {
visit.visit(this);
}
}
public class BFuncationImpl implements Accept {
@Override
public void accept(Visit visit) {
visit.visit(this);
}
}
public class CFuncationImpl implements Accept {
@Override
public void accept(Visit visit) {
visit.visit(this);
}
}
测试代码:
java
public class Main {
public static void main(String[] args) {
List<Accept> acceptList = List.of(new AFuncationImpl(), new BFuncationImpl(), new CFuncationImpl());
Visit visit = new Funcation1Visitor();
for (Accept accept : acceptList) {
accept.accept(visit);
}
visit = new Funcation2Visitor();
for (Accept accept : acceptList) {
accept.accept(visit);
}
}
}
类之间关系如图:

梳理一下改动的思路,由于接口中的方法越来越多会导致实现类的代码越来越多,访问者模式提供了一种思路按照功能进行抽象(Visit),利用接口重载为所有的实现类设置一个方法(Visit.visit),并让所有的功能类实现这个方法。
由于功能类实现后正常情况下无法利用多态特性(此时的参数是具体实现类),因此可以让具体实现类(AFuncationImpl、BFuncationImpl等)实现一个统一的接口(Accept)。
访问者模式为了避免不断添加功能导致类不断膨胀,职责越来越不单一,以及避免频繁地添加功能导致的频繁代码修改,将对象与操作解耦,将这些业务操作抽离出来,定义在独立细分的访问者类中。