C++访问者模式是否支持对多个项目内的类执行访问操作?
关于你的初始判断
你的判断不准确。传统访问者模式完全可以支持跨项目访问不同节点类,核心是要把访问者抽象接口层和节点基类统一放在公共依赖组件中,而具体访问者实现放在上层业务项目中,不会出现循环依赖问题。
你现有代码的结构已经接近正确方案,唯一的问题是你把Visitor的具体实现放在了和Node、NodeA/NodeB同层的位置,才会出现依赖矛盾:如果Visitor的实现同时依赖NodeA和NodeB,放在公共层就会强制所有依赖公共层的项目都同时依赖ProjectA和ProjectB。
更优的解决方案
除了你提到的两种方案,还有三种更常用的低侵入实现方式,可根据项目技术栈选择:
1. 拆分公共抽象层方案(最推荐,符合经典访问者模式)
调整依赖结构即可完全解决跨项目问题,不需要修改现有节点类的核心逻辑:
- 新增一个无额外依赖的公共基础项目
Common,存放三个内容:Node基类定义、纯虚Visitor接口类(仅声明纯虚visit接口,不需要实现)、NodeA/NodeB的前向声明 - ProjectA、ProjectB仅依赖
Common,分别实现NodeA::accept和NodeB::accept,方法内部直接调用v.visit(*this)即可,不需要依赖其他项目 - 具体的访问者实现放在同时依赖Common、ProjectA、ProjectB的上层业务项目中,继承
Visitor基类实现所有visit方法,完全不需要修改ProjectA、ProjectB的现有代码
示例代码调整如下:
/// --- Common/Visitor.h 公共层头文件 --- class Node; class NodeA; class NodeB; class Visitor { public: virtual ~Visitor() = default; virtual void visit(const Node&) = 0; virtual void visit(const NodeA&) = 0; virtual void visit(const NodeB&) = 0; };
/// --- ProjectA/NodeA.cpp 仅依赖公共层 --- #include "NodeA.h" #include "Visitor.h" void NodeA::accept(Visitor& v) { v.visit(*this); }
/// --- 上层业务项目/MyConcreteVisitor.h 同时依赖三个项目 --- #include "Visitor.h" #include "NodeA.h" #include "NodeB.h" class MyConcreteVisitor : public Visitor { public: void visit(const Node& node) override { /* 业务实现 */ } void visit(const NodeA& node) override { /* 业务实现 */ } void visit(const NodeB& node) override { /* 业务实现 */ } };
2. 非循环访问者(Acyclic Visitor)模式
如果项目后续会频繁新增节点类型,不想每次新增节点都修改公共Visitor接口,可以用该变种模式:
- 公共层仅存放空的
Visitor基类,以及每个节点类型对应的独立访问接口(比如INodeAVisitor、INodeBVisitor) - 具体访问者需要访问哪种节点,就继承对应的访问接口,
accept方法内通过动态类型转换判断访问者是否支持该节点的访问,不需要跨项目依赖
缺点是存在运行时类型转换的开销,优点是完全解耦不同项目的节点和访问者。
3. std::variant + 访问器模式(C++17及以上推荐)
如果你的节点类不需要继承关系,或者可以改造成variant存储,直接用C++17标准库的std::visit实现访问者,完全不需要定义accept方法,也不会有跨项目依赖问题:
- 定义
using NodeVariant = std::variant<NodeA, NodeB, 其他节点类型> - 业务层实现访问器结构体,重载
operator()处理不同的节点类型,直接调用std::visit(访问器实例, 节点变量)即可
该方案是最简洁的,不需要继承也不需要虚函数,唯一要求是使用访问者的位置能拿到所有节点类型的定义。
内容的提问来源于stack exchange,提问作者Gábor Pálovics
相关产品推荐
相关产品推荐

