初识设计模式 - 访问者模式
简介
访问者设计模式(Visitor Design Pattern)的定义是,允许一个或多个操作应用到一组对象上,解耦操作和对象本身。
在使用访问者模式的时候,被访问的元素通常不是单独存在的,它们存储在一个集合中,这个集合称为“对象结构”,访问者通过遍历对象结构实现对其存储的元素进行逐个访问。
访问者模式使用了“双重分派”的调用机制,即元素对象定义一个操作方法支持注入访问者对象,在操作方法内调用访问者的访问方法,并将当前元素对象传入到访问方法中。
具体实现
在这里举一个工作当中的具体例子,在小公司的项目组当中,名义上区分了开发、测试等岗位,但实际上开发人员既要会开发,也有会测试,对于测试人员也是同样的要求,既要会测试,也要会开发。
在这里案例当中,开发人员、测试人员统称为元素,我们在这里先构建一个抽象的元素类。其代码示例如下:
public interface Element {
// 定义一个接受访问者访问的抽象方法
void accept(Visitor visitor);
}
对于开发人员类,根据自己的情况实现这个 accept()
方法,其代码如下:
public class Programmer implements Element {
private String name = "开发人员";
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Visitor visitor) {
visitor.visitProgrammer(this);
}
}
对于测试人员,根据自己的情况实现这个 accept()
方法,其代码如下:
public class Tester implements Element {
private String name = "测试人员";
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Visitor visitor) {
visitor.visitTester(this);
}
}
第二步,最重要的就是要定义好一个访问者类,在这里抽象出的访问者接口可以是以项目组为范围,为项目组中的每一个元素定义对应的访问方法。其代码示例如下:
public interface Visitor {
void visitProgrammer(Programmer programmer);
void visitTester(Tester tester);
}
每当出现一个新的操作时,就可以实现访问者接口,注入不同的元素对象以实现不同的操作。
如下是开发人员和测试人员使用开发技能的代码示例:
public class DevelopVisitor implements Visitor {
@Override
public void visitProgrammer(Programmer programmer) {
System.out.println(programmer.getName() + "在开发");
}
@Override
public void visitTester(Tester tester) {
System.out.println(tester.getName() + "在开发");
}
}
如下是开发人员和测试人员使用测试技能的代码示例:
public class TestVisitor implements Visitor {
@Override
public void visitProgrammer(Programmer programmer) {
System.out.println(programmer.getName() + "在测试");
}
@Override
public void visitTester(Tester tester) {
System.out.println(tester.getName() + "在测试");
}
}
总结
优点
访问者模式的主要优点如下:
- 能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能,符合开闭原则
- 将有关元素的行为都封装到一个访问者对象中,每个访问者对象的功能都比较单一,符合单一职责原则
缺点
访问者模式的主要缺点如下:
- 增加新的元素类需要在每一个访问者类中都增加相应的具体操作,这违背了开闭原则
- 访问者对象可以访问并调用每一个元素对象的操作,这意味着元素对象有时候会暴露一些内部操作和内部状态,破坏了封装
- 访问者模式依赖了具体类,而没有依赖抽象类,违反了依赖倒置原则
适用场景
访问者模式的适用场景如下:
- 对象结构中元素对象的类很少改变,但经常需要在此对象结构上定义新的操作
源码
访问者模式提供一个方便的可维护的方式来操作一组对象,JDK 内置了这样的元素接口和访问者接口。
如下是元素接口 javax.lang.model.element.Element
的部分代码:
public interface Element extends javax.lang.model.AnnotatedConstruct {
<R, P> R accept(ElementVisitor<R, P> v, P p);
}
如下是访问者接口 javax.lang.model.element.ElementVisitor
的部分代码:
public interface ElementVisitor<R, P> {
R visit(Element e, P p);
default R visit(Element e) {
return visit(e, null);
}
}