访问者设计模式是一种行为型设计模式,它的核心目的是将数据结构与作用于数据结构上的操作解耦,使得可以在不修改原有数据结构类的前提下,为数据结构中的元素添加新的操作。这种模式非常适合处理稳定数据结构、多变操作的场景,能够有效避免频繁修改已有类的代码带来的风险。

访问者设计模式的核心角色
要实现访问者设计模式,需要先明确模式中的几个核心角色,每个角色承担不同的职责:
- 抽象访问者(Visitor):声明所有具体访问者需要实现的访问方法,每个方法对应一个具体元素类型。
- 具体访问者(ConcreteVisitor):实现抽象访问者声明的所有方法,定义对每个具体元素的具体操作逻辑。
- 抽象元素(Element):定义一个接受访问者的方法,通常以
accept(Visitor* visitor)的形式存在。 - 具体元素(ConcreteElement):实现抽象元素的
accept方法,在方法内部调用访问者对应的访问方法,把自己作为参数传入。 - 对象结构(ObjectStructure):管理元素集合,可以遍历所有元素,让访问者访问每个元素。
C++实现访问者设计模式的步骤
1. 定义抽象元素类
首先定义抽象元素基类,声明accept方法,参数是抽象访问者指针:
#include <iostream>
#include <vector>
#include <memory>
// 前置声明抽象访问者类
class Visitor;
// 抽象元素类
class Element {
public:
virtual ~Element() = default;
// 接受访问者的方法
virtual void accept(Visitor* visitor) = 0;
};
2. 定义具体元素类
定义两个具体元素类,实现accept方法,在方法内部调用访问者对应的访问方法:
// 具体元素A
class ConcreteElementA : public Element {
public:
void accept(Visitor* visitor) override;
// 元素A特有的方法
void operationA() {
std::cout << "执行ConcreteElementA的特有操作" << std::endl;
}
};
// 具体元素B
class ConcreteElementB : public Element {
public:
void accept(Visitor* visitor) override;
// 元素B特有的方法
void operationB() {
std::cout << "执行ConcreteElementB的特有操作" << std::endl;
}
};
3. 定义抽象访问者类
抽象访问者类声明对所有具体元素的访问方法:
// 抽象访问者类
class Visitor {
public:
virtual ~Visitor() = default;
// 访问具体元素A的方法
virtual void visit(ConcreteElementA* element) = 0;
// 访问具体元素B的方法
virtual void visit(ConcreteElementB* element) = 0;
};
4. 实现具体访问者类
具体访问者实现抽象访问者的方法,定义对每个元素的具体操作:
// 具体访问者1:实现元素的信息打印操作
class PrintVisitor : public Visitor {
public:
void visit(ConcreteElementA* element) override {
std::cout << "PrintVisitor访问ConcreteElementA:" << std::endl;
element->operationA();
}
void visit(ConcreteElementB* element) override {
std::cout << "PrintVisitor访问ConcreteElementB:" << std::endl;
element->operationB();
}
};
// 具体访问者2:实现元素的计数操作
class CountVisitor : public Visitor {
public:
CountVisitor() : elementACount(0), elementBCount(0) {}
void visit(ConcreteElementA* element) override {
elementACount++;
std::cout << "CountVisitor统计到ConcreteElementA,当前数量:" << elementACount << std::endl;
}
void visit(ConcreteElementB* element) override {
elementBCount++;
std::cout << "CountVisitor统计到ConcreteElementB,当前数量:" << elementBCount << std::endl;
}
private:
int elementACount;
int elementBCount;
};
5. 补充具体元素的accept方法实现
因为具体元素的accept方法需要调用访问者的visit方法,所以需要在访问者类定义之后补充实现:
void ConcreteElementA::accept(Visitor* visitor) {
visitor->visit(this);
}
void ConcreteElementB::accept(Visitor* visitor) {
visitor->visit(this);
}
6. 定义对象结构类
对象结构类管理元素集合,提供遍历元素让访问者访问的能力:
// 对象结构类
class ObjectStructure {
public:
void addElement(std::shared_ptr<Element> element) {
elements.push_back(element);
}
// 让访问者访问所有元素
void accept(Visitor* visitor) {
for (auto& element : elements) {
element->accept(visitor);
}
}
private:
std::vector<std::shared_ptr<Element>> elements;
};
7. 测试代码验证效果
编写测试代码,验证访问者模式的效果,添加新的访问者不需要修改原有元素类的代码:
int main() {
// 创建对象结构,添加元素
ObjectStructure objStructure;
objStructure.addElement(std::make_shared<ConcreteElementA>());
objStructure.addElement(std::make_shared<ConcreteElementB>());
objStructure.addElement(std::make_shared<ConcreteElementA>());
// 使用打印访问者
PrintVisitor printVisitor;
std::cout << "===== 使用打印访问者 =====" << std::endl;
objStructure.accept(&printVisitor);
// 使用计数访问者,不需要修改任何元素类的代码
CountVisitor countVisitor;
std::cout << "n===== 使用计数访问者 =====" << std::endl;
objStructure.accept(&countVisitor);
return 0;
}
模式优势与适用场景
访问者设计模式的最大优势就是符合开闭原则,当需要添加新的操作时,只需要新增具体访问者类,不需要修改已有的元素类、对象结构类的代码,避免了修改原有代码带来的风险。
该模式适合以下场景:
- 数据结构稳定,但经常需要在这个数据结构上定义新的操作。
- 需要对一个数据结构中的对象进行很多不同的且不相关的操作,希望避免这些操作污染这些对象的类。
- 需要遍历多个不同类型的对象,并且对每个类型执行不同的操作。
不过该模式也有局限性,如果元素类的类型经常变化,需要频繁修改抽象访问者和所有具体访问者的代码,此时就不适合使用该模式。