访问者模式

发布于:2025-03-23 ⋅ 阅读:(19) ⋅ 点赞:(0)

新增访问者指的是新增继承Visitor的子类

下面给出访问者模式(Visitor Pattern)的几个好处,每个好处附带一个示例及相应的
代码。


  1. 增加新的操作更容易
    【好处说明】
    在不修改现有元素类的前提下,可通过新增访问者类来扩展新的功能。例如,给图形对象增加计算面积的操作。
#include <iostream>
#include <vector>

// 前向声明
class Circle;
class Rectangle;

// Visitor 接口
class Visitor {
public:
    virtual void Visit(Circle* circle) = 0;
    virtual void Visit(Rectangle* rectangle) = 0;
};

// Element 接口
class Shape {
public:
    virtual void Accept(Visitor* visitor) = 0;
};

// Element 子类 Circle
class Circle : public Shape {
public:
    double radius;
    Circle(double r) : radius(r) {}
    void Accept(Visitor* visitor) override {
        visitor->Visit(this);
    }
};

// Element 子类 Rectangle
class Rectangle : public Shape {
public:
    double width, height;
    Rectangle(double w, double h) : width(w), height(h) {}
    void Accept(Visitor* visitor) override {
        visitor->Visit(this);
    }
};

// ConcreteVisitor 增加计算面积操作
class AreaVisitor : public Visitor {
public:
    void Visit(Circle* circle) override {
        double area = 3.14159 * circle->radius * circle->radius;
        std::cout << "Circle area: " << area << std::endl;
    }
    void Visit(Rectangle* rectangle) override {
        double area = rectangle->width * rectangle->height;
        std::cout << "Rectangle area: " << area << std::endl;
    }
};

int main() {
    std::vector<Shape*> shapes;
    shapes.push_back(new Circle(5.0));
    shapes.push_back(new Rectangle(4.0, 6.0));

    AreaVisitor areaVisitor;
    for (auto shape : shapes) {
        shape->Accept(&areaVisitor);
    }

    for (auto shape : shapes)
        delete shape;

    return 0;
}

  1. 分离无关的行为
    【好处说明】
    将不同的操作逻辑分离到独立的访问者中,使元素类只负责数据结构和接受访问者,而将具体操作逻辑移到访问者类中。比如,绘制和导出操作分别由不同访问者实现。
#include <iostream>

// 使用前面的 Shape、Circle、Rectangle 声明(略)

// ConcreteVisitor 实现绘制操作
class DrawVisitor : public Visitor {
public:
    void Visit(Circle* circle) override {
        std::cout << "Drawing Circle with radius " << circle->radius << std::endl;
    }
    void Visit(Rectangle* rectangle) override {
        std::cout << "Drawing Rectangle " << rectangle->width << "x" << rectangle->height << std::endl;
    }
};

int main() {
    // 假设已有 shapes 数组
    Circle circle(3.0);
    Rectangle rectangle(2.0, 5.0);

    DrawVisitor drawVisitor;
    circle.Accept(&drawVisitor);
    rectangle.Accept(&drawVisitor);

    return 0;
}

  1. 集中相关行为
    【好处说明】
    所有与特定操作相关的逻辑被集中放在同一个访问者中,便于理解和维护。比如所有绘制操作都集中在 DrawVisitor 中。

上面第二个示例中,DrawVisitor 就集中处理了绘制逻辑,不必分散在每个图形类中,代码复用和修改都更方便。


  1. 增加新的访问者
    【好处说明】
    当需要添加新的功能时,只需创建新的访问者类,而无需更改现有的元素类。例如,增加一个导出图形数据的操作。
#include <iostream>

// 使用前面的 Shape、Circle、Rectangle 声明(略)

// ConcreteVisitor 实现导出操作
class ExportVisitor : public Visitor {
public:
    void Visit(Circle* circle) override {
        std::cout << "Exporting Circle data (radius=" << circle->radius << ")" << std::endl;
    }
    void Visit(Rectangle* rectangle) override {
        std::cout << "Exporting Rectangle data (width=" << rectangle->width 
                  << ", height=" << rectangle->height << ")" << std::endl;
    }
};

int main() {
    // 假设已有 shapes 实例
    Circle circle(7.0);
    Rectangle rectangle(3.0, 8.0);

    ExportVisitor exportVisitor;
    circle.Accept(&exportVisitor);
    rectangle.Accept(&exportVisitor);

    return 0;
}

  1. 简化元素类
    【好处说明】
    元素类只需实现接受访问者的方法,不必包含特定操作逻辑,这使得它们的职责单一。例如,Circle 和 Rectangle 类只需提供 Accept 方法,实际操作延迟给访问者完成。
#include <iostream>

// 简化后的 Shape 类及其子类定义
class Shape {
public:
    virtual void Accept(Visitor* visitor) = 0;
};

class Circle : public Shape {
public:
    double radius;
    Circle(double r) : radius(r) {}
    void Accept(Visitor* visitor) override {
        visitor->Visit(this);
    }
};

class Rectangle : public Shape {
public:
    double width, height;
    Rectangle(double w, double h) : width(w), height(h) {}
    void Accept(Visitor* visitor) override {
        visitor->Visit(this);
    }
};

// 这里只定义 Accept 方法,具体操作由不同的访问者完成。

// 示例调用见前面的各个示例代码

通过这几个示例可以看出,访问者模式将操作逻辑与数据结构分离,使得新增操作变得简单且不会影响原有的类结构,同时使各个具体操作高度集中、易于维护。