C++ 访问者模式详解
访问者模式(Visitor Pattern)是一种行为设计模式,它允许你将算法与对象结构分离,使得可以在不修改现有对象结构的情况下定义新的操作。
核心概念
设计原则
访问者模式遵循以下设计原则:
-
开闭原则:可以添加新访问者而不修改元素类
-
单一职责原则:将相关行为集中到访问者中
-
双重分发:利用双分派技术实现动态绑定
主要优点
-
算法集中:将相关行为集中在一个访问者对象中
-
灵活扩展:容易添加新的操作
-
跨类操作:可以对不同类的对象执行统一操作
-
数据分离:将数据结构与数据操作分离
模式结构
主要组件
-
Visitor(访问者接口)
-
为每个具体元素类声明访问操作
-
-
ConcreteVisitor(具体访问者)
-
实现每个访问操作
-
-
Element(元素接口)
-
定义accept方法接受访问者
-
-
ConcreteElement(具体元素)
-
实现accept方法
-
-
ObjectStructure(对象结构)
-
维护元素集合
-
提供遍历元素的方法
-
完整代码示例
#include <iostream>
#include <vector>
#include <memory>
#include <string>// 前向声明
class ConcreteElementA;
class ConcreteElementB;// ==================== 访问者接口 ====================
class Visitor {
public:virtual void visit(ConcreteElementA* element) = 0;virtual void visit(ConcreteElementB* element) = 0;virtual ~Visitor() = default;
};// ==================== 元素接口 ====================
class Element {
public:virtual void accept(Visitor* visitor) = 0;virtual ~Element() = default;
};// ==================== 具体元素A ====================
class ConcreteElementA : public Element {std::string name_;
public:explicit ConcreteElementA(const std::string& name) : name_(name) {}void accept(Visitor* visitor) override {visitor->visit(this);}std::string getName() const { return name_; }// 元素特有的操作std::string operationA() const {return "元素A特有操作";}
};// ==================== 具体元素B ====================
class ConcreteElementB : public Element {int value_;
public:explicit ConcreteElementB(int value) : value_(value) {}void accept(Visitor* visitor) override {visitor->visit(this);}int getValue() const { return value_; }// 元素特有的操作std::string operationB() const {return "元素B特有操作";}
};// ==================== 具体访问者1 ====================
class ConcreteVisitor1 : public Visitor {
public:void visit(ConcreteElementA* element) override {std::cout << "访问者1访问" << element->getName() << ": " << element->operationA() << std::endl;}void visit(ConcreteElementB* element) override {std::cout << "访问者1访问元素B(值=" << element->getValue() << "): " << element->operationB() << std::endl;}
};// ==================== 具体访问者2 ====================
class ConcreteVisitor2 : public Visitor {
public:void visit(ConcreteElementA* element) override {std::cout << "访问者2记录元素A: " << element->getName() << std::endl;}void visit(ConcreteElementB* element) override {std::cout << "访问者2处理元素B值: " << element->getValue() * 2 << std::endl;}
};// ==================== 对象结构 ====================
class ObjectStructure {std::vector<std::unique_ptr<Element>> elements_;public:void addElement(std::unique_ptr<Element> element) {elements_.push_back(std::move(element));}void accept(Visitor* visitor) {for (const auto& element : elements_) {element->accept(visitor);}}
};// ==================== 客户端代码 ====================
int main() {std::cout << "=== 访问者模式演示 ===" << std::endl;// 创建对象结构ObjectStructure structure;structure.addElement(std::make_unique<ConcreteElementA>("测试元素A"));structure.addElement(std::make_unique<ConcreteElementB>(42));structure.addElement(std::make_unique<ConcreteElementA>("另一个元素A"));structure.addElement(std::make_unique<ConcreteElementB>(100));// 创建访问者ConcreteVisitor1 visitor1;ConcreteVisitor2 visitor2;// 使用不同访问者访问结构std::cout << "\n使用访问者1:" << std::endl;structure.accept(&visitor1);std::cout << "\n使用访问者2:" << std::endl;structure.accept(&visitor2);return 0;
}
模式变体
1. 带返回值的访问者
class ComputingVisitor : public Visitor {int total_ = 0;
public:void visit(ConcreteElementA* element) override {total_ += element->getName().length();}void visit(ConcreteElementB* element) override {total_ += element->getValue();}int getTotal() const { return total_; }
};// 使用示例
ComputingVisitor computingVisitor;
structure.accept(&computingVisitor);
std::cout << "计算总和: " << computingVisitor.getTotal() << std::endl;
2. 基于模板的访问者
template <typename... Types>
class GenericVisitor;template <typename T>
class GenericVisitor<T> {
public:virtual void visit(T* element) = 0;virtual ~GenericVisitor() = default;
};template <typename T, typename... Types>
class GenericVisitor<T, Types...> : public GenericVisitor<Types...> {
public:using GenericVisitor<Types...>::visit;virtual void visit(T* element) = 0;
};// 定义元素接口
class Element {
public:template <typename... Types>void accept(GenericVisitor<Types...>* visitor) {if (auto* v = dynamic_cast<GenericVisitor<ConcreteElementA, ConcreteElementB>*>(visitor)) {if (auto* elem = dynamic_cast<ConcreteElementA*>(this)) {v->visit(elem);} else if (auto* elem = dynamic_cast<ConcreteElementB*>(this)) {v->visit(elem);}}}
};
实际应用场景
-
编译器设计:语法树的不同遍历方式(类型检查、代码生成等)
-
文档处理:对文档结构的不同操作(渲染、字数统计等)
-
UI组件:对复杂UI结构的操作(样式应用、布局计算等)
-
游戏开发:游戏对象的不同处理(渲染、物理计算等)
-
财务系统:财务数据的不同分析(税务计算、报表生成等)