访问者模式

访问者设计模式是一种行为型设计模式,可以在不修改被访问元素类的情况下,增加对被访问元素的新操作。它通过将算法与操作对象分离,使得可以在不修改现有元素类的情况下增加新的操作。当然是基于现有元素的属性。

  1. 访问者(Visitor):定义了可以访问和操作每个具体元素的接口。访问者模式通常会为每一种元素类型都定义一个对应的访问者接口方法。
  2. 具体访问者(ConcreteVisitor):实现了访问者接口,提供了对具体元素的具体操作。每个具体访问者都实现了在访问元素时应该执行的操作。
  3. 元素(Element):定义了一个接受访问者的接口,通常包含一个 accept 方法,该方法接收一个访问者作为参数。
  4. 具体元素(ConcreteElement):实现了元素接口,定义了具体元素的结构和行为。对于每个具体元素,都会有一个 accept 方法,该方法将具体元素传递给访问者。

考虑一种场景:现有一组图形,想计算每个图形的周长,然后加起来得到总周长。不使用访问者模式,在每个图形里面增加一个方法计算周长,然后加起来。过一段时间,有一个新需求,针对每个图形计算其面积,又要在每个类里增加一个方法计算面积再加起来。如果使用访问者模式,计算周长时,改一遍,计算面积时,每个图形类的代码就不用动了,在访问者类中增加方法就行了。具体看下面的代码:

第一次的代码,有圆形和长方形,计算周长

#include <iostream>
#include <cmath>

class Circle;
class Rectangle;

// 访问者接口
class ShapeVisitor {
public:
    virtual void visit(const Circle& circle) = 0;
    virtual void visit(const Rectangle& rectangle) = 0;
    virtual ~ShapeVisitor() {}
};

// 具体访问者:计算周长
class PerimeterVisitor : public ShapeVisitor {
private:
    double totalPerimeter;

public:
    PerimeterVisitor() : totalPerimeter(0) {}

    double getTotalPerimeter() const {
        return totalPerimeter;
    }

    void visit(const Circle& circle) override {
        double perimeter = 2 * M_PI * circle.getRadius();
        totalPerimeter += perimeter;
    }

    void visit(const Rectangle& rectangle) override {
        double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());
        totalPerimeter += perimeter;
    }
};

// 元素接口
class Shape {
public:
    virtual void accept(ShapeVisitor& visitor) const = 0;
    virtual ~Shape() {}
};

// 具体元素:圆形
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double radius) : radius(radius) {}

    double getRadius() const {
        return radius;
    }

    void accept(ShapeVisitor& visitor) const override {
        visitor.visit(*this);
    }
};

// 具体元素:矩形
class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double width, double height) : width(width), height(height) {}

    double getWidth() const {
        return width;
    }

    double getHeight() const {
        return height;
    }

    void accept(ShapeVisitor& visitor) const override {
        visitor.visit(*this);
    }
};

// 客户端代码
int main() {
    Circle circle(5);
    Rectangle rectangle(4, 6);

    PerimeterVisitor visitor;

    circle.accept(visitor);
    rectangle.accept(visitor);

    std::cout << "Total Perimeter: " << visitor.getTotalPerimeter() << std::endl;

    return 0;
}

第二次的代码,增加面积计算

class AreaVisitor : public ShapeVisitor {
private:
    double totalArea;
public:
    AreaAndPerimeterVisitor() : totalArea(0) {}

    double getTotalArea() const {
        return totalArea;
    }

    void visit(const Circle& circle) override {
        double area = M_PI * pow(circle.getRadius(), 2);
        totalArea += area;
    }

    void visit(const Rectangle& rectangle) override {
        double area = rectangle.getWidth() * rectangle.getHeight();
        totalArea += area;
    }
};

// 客户端代码
int main() {
    Circle circle(5);
    Rectangle rectangle(4, 6);

    PerimeterVisitor visitor;

    circle.accept(visitor);
    rectangle.accept(visitor);

    AreaVisitor visitor1;

    circle.accept(visitor1);
    rectangle.accept(visitor1);

    std::cout << "Total Perimeter: " << visitor.getTotalPerimeter() << std::endl;
    std::cout << "Total Area: " << visitor1.getTotalArea() << std::endl;

    return 0;
}