访问者模式详解
概念
访问者模式(Visitor Pattern)是一种行为型设计模式,用于将数据结构与其操作解耦。通过在不改变数据结构的前提下,增加新的操作,访问者模式提供了一种灵活的方式来实现功能扩展。
关键特性
- 分离操作:将具体操作从对象本身分离,使得操作逻辑集中在访问者中。
- 支持扩展:容易增加新的操作,但增加新的数据结构时会比较复杂。
- 双分派机制:通过在访问者和元素之间的双向调用实现。
适用场景
- 需要对对象结构中的元素执行多种操作:每种操作需要在对象类型之间有区别。
- 需要频繁扩展操作:在已有的对象结构中增加新功能时无需修改数据结构。
- 不希望操作逻辑和数据结构耦合:将操作集中在访问者中,数据结构更清晰。
使用案例
1. 编译器中的语法树
- 场景:对语法树中的不同节点(如函数、变量)进行语义检查、代码生成等操作。
- 解决:语法树节点实现
Element
接口,不同的操作通过访问者完成。
2. 文件系统
- 场景:对文件和目录执行操作,如统计大小、权限检查等。
- 解决:文件和目录实现
Element
接口,具体操作用访问者实现。
3. 游戏场景
- 场景:在游戏中,为不同类型的单位(如士兵、坦克)计算攻击力、升级逻辑。
- 解决:单位类实现
Element
接口,计算攻击力或升级功能使用访问者完成。
优缺点
优点 | 缺点 |
增加操作时无须修改数据结构,符合开闭原则 | 添加新的数据结构类型需要修改所有访问者类,维护成本高 |
使得操作集中,便于管理和维护 | 数据结构与访问者模式之间存在依赖 |
支持双分派,允许根据运行时类型执行操作 | 对数据结构的要求较高,必须稳定不变 |
类图
C++实现
#include <iostream>
#include <vector>
#include <memory>
class ConcreteElementA;
class ConcreteElementB;
// 访问者接口
class Visitor {
public:
virtual void Visit(ConcreteElementA* element) = 0;
virtual void Visit(ConcreteElementB* element) = 0;
};
// 元素接口
class Element {
public:
virtual ~Element() = default;
virtual void Accept(Visitor* visitor) = 0;
};
// 具体元素A
class ConcreteElementA : public Element {
public:
void OperationA() {
std::cout << "ConcreteElementA OperationA\n";
}
void Accept(Visitor* visitor) override {
visitor->Visit(this);
}
};
// 具体元素B
class ConcreteElementB : public Element {
public:
void OperationB() {
std::cout << "ConcreteElementB OperationB\n";
}
void Accept(Visitor* visitor) override {
visitor->Visit(this);
}
};
// 具体访问者
class ConcreteVisitor : public Visitor {
public:
void Visit(ConcreteElementA* element) override {
std::cout << "Visiting ConcreteElementA\n";
element->OperationA();
}
void Visit(ConcreteElementB* element) override {
std::cout << "Visiting ConcreteElementB\n";
element->OperationB();
}
};
// 对象结构
class ObjectStructure {
private:
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() {
ObjectStructure structure;
structure.AddElement(std::make_unique<ConcreteElementA>());
structure.AddElement(std::make_unique<ConcreteElementB>());
ConcreteVisitor visitor;
structure.Accept(&visitor);
return 0;
}
C#实现
using System;
using System.Collections.Generic;
// Visitor Interface
public interface IVisitor {
void Visit(ConcreteElementA element);
void Visit(ConcreteElementB element);
}
// Element Interface
public abstract class Element {
public abstract void Accept(IVisitor visitor);
}
// Concrete Element A
public class ConcreteElementA : Element {
public override void Accept(IVisitor visitor) {
visitor.Visit(this);
}
public void OperationA() {
Console.WriteLine("ConcreteElementA OperationA");
}
}
// Concrete Element B
public class ConcreteElementB : Element {
public override void Accept(IVisitor visitor) {
visitor.Visit(this);
}
public void OperationB() {
Console.WriteLine("ConcreteElementB OperationB");
}
}
// Concrete Visitor
public class ConcreteVisitor : IVisitor {
public void Visit(ConcreteElementA element) {
Console.WriteLine("Visiting ConcreteElementA");
element.OperationA();
}
public void Visit(ConcreteElementB element) {
Console.WriteLine("Visiting ConcreteElementB");
element.OperationB();
}
}
// Object Structure
public class ObjectStructure {
private readonly List<Element> _elements = new List<Element>();
public void AddElement(Element element) {
_elements.Add(element);
}
public void Accept(IVisitor visitor) {
foreach (var element in _elements) {
element.Accept(visitor);
}
}
}
// Example Usage
class Program {
static void Main(string[] args) {
var structure = new ObjectStructure();
structure.AddElement(new ConcreteElementA());
structure.AddElement(new ConcreteElementB());
var visitor = new ConcreteVisitor();
structure.Accept(visitor);
}
}