c++如何实现访问者设计模式_c++在不修改类的情况下添加新操作

访问者设计模式通过分离数据结构与操作,允许在不修改原有类的情况下添加新行为。1. 定义Visitor接口声明对各元素的访问方法;2. 元素类实现accept方法,接收访问者并调用其对应函数;3. 具体访问者实现不同操作逻辑。例如图形类Shape通过accept接受DrawVisitor或SaveVisitor,实现绘制与保存功能,符合开闭原则,适用于结构稳定、行为多变的场景。

在C++中,访问者设计模式允许你在不修改原有类结构的前提下为这些类添加新的操作。这种模式特别适用于类结构相对稳定,但需要频繁增加新行为的场景。

访问者模式的核心思想

将数据结构与作用于其上的操作分离。通过引入一个访问者接口,把对各类对象的操作封装到访问者中,从而避免不断修改原始类来添加功能。

关键角色包括:

  • Visitor(访问者):定义对每个具体元素的访问方法。
  • Element(元素):声明接受访问者的方法,通常为accept(Visitor&)
  • ConcreteElement:实现accept方法,调用访问者的对应函数。
  • ConcreteVisitor:实现具体操作逻辑。

基本实现步骤

假设我们有一组图形类(如圆形、矩形),想在不改动它们的情况下支持“绘制”和“保存”等新操作。

1. 定义元素接口

所有可被访问的类都继承自同一个基类:

class Shape;

class ShapeVisitor { public: virtual void visitCircle(const Circle circle) = 0; virtual void visitRectangle(const Rectangle rectangle) = 0; virtual ~ShapeVisitor() = default; };

class Shape { public: virtual void accept(ShapeVisitor& visitor) const = 0; virtual ~Shape() = default; };

2. 实现具体元素

每个子类实现accept,反向调用访问者对应方法:

class Circle : public Shape {
public:
    void accept(ShapeVisitor& visitor) const override {
        visitor.visitCircle(this);
    }
    double radius() const { return 1.0; }
};

class Rectangle : public Shape { public: void accept(ShapeVisitor& visitor) const override { visitor.visitRectangle(this); } double width() const { return 2.0; } double height() const { return 3.0; } };

3. 创建具体访问者

新增操作只需添加新的访问者类:

class DrawVisitor : public ShapeVisitor {
public:
    void visitCircle(const Circle* circle) override {
        std::cout << "Drawing a circle with radius " << circle->radius() << "\n";
    }
void visitRectangle(const Rectangle* rectangle) override {
    std::cout << "Drawing a rectangle " 
              << rectangle->width() << "x" << rectangle->height() << "\n";
}

};

class SaveVisitor : public ShapeVisitor { public: void visitCircle(const Circle* circle) override { std::cout radius()

void visitRectangle(const Rectangle* rectangle) override {
    std::cout << "Saving rectangle data: "
              << rectangle->width() << "," << rectangle->height() << "\n";
}

};

使用方式示例

客户端代码可以灵活切换不同操作:

std::vector> shapes;
shapes.push_back(std::make_unique());
shapes.push_back(std::make_unique());

DrawVisitor drawVisitor; SaveVisitor saveVisitor;

for (const auto& shape : shapes) { shape->accept(drawVisitor); // 执行绘制 }

for (const auto& shape : shapes) { shape->accept(saveVisitor); // 执行保存 }

这样,每当需要新增功能(比如计算面积、导出JSON),只需写一个新的访问者类,无需改动任何已有Shape相关代码。

基本上就这些。只要类提供了accept接口,后续扩展操作就很方便,符合开闭原则。缺点是如果元素类型经常变动,维护访问者的双分派会变得繁琐。但在结构稳定时,这是解耦数据与行为的有效手段。不复杂但容易忽略细节。