无环访问者模式真的不如传统访问者模式吗?
无环访问者模式的正确性保障与使用意义
我查阅了多篇对比无环访问者模式(Acyclic Visitor Pattern)与传统访问者模式(Visitor Pattern)优势的文章和回答,本文所指的无环访问者模式以鲍勃大叔的定义为准。
无环访问者模式的核心是将新增被访问元素的操作,与更新所有访问者的需求解耦,允许访问者和元素独立扩展、部署。但这里有个核心疑问:实现新的被访问元素时,如何保证访问者的正确性?
假设新增被访问元素通常需要修改现有访问者才能保持逻辑正确,那这个模式就存在严重问题:每次新增元素,开发者都得手动检查并更新所有相关访问者,没法依赖编译器强制校验。这相当于把传统访问者模式中的编译时错误,转化为无环访问者模式下的软件缺陷,直接违背了“组件可独立开发部署”的承诺。在访问者数量庞大、多人独立开发访问者功能的大型项目里,这种模式可能导致需要付出高昂成本才能修复的线上bug。
是否存在非手动的方法缓解该问题?如果没有,为何要使用无环访问者模式?
基础示例
假设我们有如下被访问层级:
public abstract class Base { public abstract void accept(Visitor v); } public class A extends Base { public void accept(Visitor v) { if (v instanceof AVisitor) { AVisitor av = (AVisitor) v; av.visit(this); } } } public class B extends Base { public void accept(Visitor v) { if (v instanceof BVisitor) { BVisitor bv = (BVisitor) v; av.visit(this); } } }
以及如下按顺序报告所有元素类型的访问者:
public interface Visitor { } public interface AVisitor { void visit(A a); } public interface BVisitor { void visit(B b); } public class ReportVisitor implements Visitor, AVisitor, BVisitor { private StringBuffer report; public ReportVisitor(StringBuffer report) { this.report = report; } public void visit(A a) { report.append("A\n"); } public void visit(B b) { report.append("B\n"); } }
现在假设我们新增一个被访问对象:
public class C extends Base { public void accept(Visitor v) { if (v instanceof CVisitor) { CVisitor cv = (AVisitor) v; cv.visit(this); } } } public interface CVisitor { void visit(C c); }
新增C的开发者必须知晓ReportVisitor及其他相关访问者,并记得修改它们。否则,ReportVisitor会直接忽略所有C的实例,这是一个缺陷。
说明
另一种修改版访问者模式通过visitOther方法实现类似解耦。该模式不在被访问对象中过滤访问者,可让需要处理所有被访问元素的访问者抛出异常,将潜在缺陷转化为运行时错误。虽然运行时错误比隐蔽的缺陷更容易发现、调试和解决,但仍远不如编译时错误可靠。
内容的提问来源于Stack Exchange,提问作者Aviv
相关产品推荐
相关产品推荐

