C++ 访问者模式详解

访问者模式(Visitor Pattern)是一种行为设计模式,它允许你将算法对象结构分离,使得可以在不修改现有对象结构的情况下定义新的操作。

核心概念

设计原则

访问者模式遵循以下设计原则:

  1. 开闭原则:可以添加新访问者而不修改元素类

  2. 单一职责原则:将相关行为集中到访问者中

  3. 双重分发:利用双分派技术实现动态绑定

主要优点

  1. 算法集中:将相关行为集中在一个访问者对象中

  2. 灵活扩展:容易添加新的操作

  3. 跨类操作:可以对不同类的对象执行统一操作

  4. 数据分离:将数据结构与数据操作分离

模式结构

主要组件

  1. Visitor(访问者接口)

    • 为每个具体元素类声明访问操作
  2. ConcreteVisitor(具体访问者)

    • 实现每个访问操作
  3. Element(元素接口)

    • 定义accept方法接受访问者
  4. ConcreteElement(具体元素)

    • 实现accept方法
  5. 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);
            }
        }
    }
};

实际应用场景

  1. 编译器设计:语法树的不同遍历方式(类型检查、代码生成等)

  2. 文档处理:对文档结构的不同操作(渲染、字数统计等)

  3. UI组件:对复杂UI结构的操作(样式应用、布局计算等)

  4. 游戏开发:游戏对象的不同处理(渲染、物理计算等)

  5. 财务系统:财务数据的不同分析(税务计算、报表生成等)

相关推荐
Niuguangshuo15 天前
Python 设计模式:访问者模式
python·设计模式·访问者模式
Pasregret16 天前
访问者模式:分离数据结构与操作的设计模式
数据结构·设计模式·访问者模式
YHY_13s16 天前
访问者模式
c++·访问者模式
全栈老李技术面试18 天前
【高频考点精讲】JavaScript中的访问者模式:从AST解析到数据转换的艺术
开发语言·前端·javascript·面试·html·访问者模式
nlog3n1 个月前
Java访问者模式详解
java·开发语言·访问者模式
郭涤生1 个月前
访问者模式_行为型_GOF23
开发语言·笔记·架构·访问者模式
cijiancao2 个月前
23 种设计模式中的访问者模式
java·后端·设计模式·访问者模式
烟沙九洲2 个月前
访问者模式
java·访问者模式
花王江不语2 个月前
访问者模式
访问者模式