C 中的访客模式示例
代替
struct IShape
{
virtual ~IShape() = default;
virtual void print() const = 0;
virtual double area() const = 0;
virtual double perimeter() const = 0;
// .. and so on
};
访客可以使用:
// The concrete shapes
struct Square;
struct Circle;
// The visitor interface
struct IShapeVisitor
{
virtual ~IShapeVisitor() = default;
virtual void visit(const Square&) = 0;
virtual void visit(const Circle&) = 0;
};
// The shape interface
struct IShape
{
virtual ~IShape() = default;
virtual void accept(IShapeVisitor&) const = 0;
};
现在具体的形状:
struct Point {
double x;
double y;
};
struct Circle : IShape
{
Circle(const Point& center, double radius) : center(center), radius(radius) {}
// Each shape has to implement this method the same way
void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
Point center;
double radius;
};
struct Square : IShape
{
Square(const Point& topLeft, double sideLength) :
topLeft(topLeft), sideLength(sideLength)
{}
// Each shape has to implement this method the same way
void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
Point topLeft;
double sideLength;
};
那么访客:
struct ShapePrinter : IShapeVisitor
{
void visit(const Square&) override { std::cout << "Square"; }
void visit(const Circle&) override { std::cout << "Circle"; }
};
struct ShapeAreaComputer : IShapeVisitor
{
void visit(const Square& square) override
{
area = square.sideLength * square.sideLength;
}
void visit(const Circle& circle) override
{
area = M_PI * circle.radius * circle.radius;
}
double area = 0;
};
struct ShapePerimeterComputer : IShapeVisitor
{
void visit(const Square& square) override { perimeter = 4. * square.sideLength; }
void visit(const Circle& circle) override { perimeter = 2. * M_PI * circle.radius; }
double perimeter = 0.;
};
并使用它:
const Square square = {{-1., -1.}, 2.};
const Circle circle{{0., 0.}, 1.};
const IShape* shapes[2] = {&square, &circle};
ShapePrinter shapePrinter;
ShapeAreaComputer shapeAreaComputer;
ShapePerimeterComputer shapePerimeterComputer;
for (const auto* shape : shapes) {
shape->accept(shapePrinter);
std::cout << " has an area of ";
// result will be stored in shapeAreaComputer.area
shape->accept(shapeAreaComputer);
// result will be stored in shapePerimeterComputer.perimeter
shape->accept(shapePerimeterComputer);
std::cout << shapeAreaComputer.area
<< ", and a perimeter of "
<< shapePerimeterComputer.perimeter
<< std::endl;
}
预期输出:
Square has an area of 4, and a perimeter of 8
Circle has an area of 3.14159, and a perimeter of 6.28319
说明 :
-
在
void Square::accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
中,this
的静态类型是已知的,因此所选择的(在编译时)重载是void IVisitor::visit(const Square&);
。 -
对于
square.accept(visitor);
呼叫,通过virtual
的动态调度用于知道要呼叫的accept
。
优点 :
- 你可以通过添加新访问者向
IShape
类添加新功能(SerializeAsXml
,…)。
缺点 :
- 添加新的具体形状(
Triangle
,…)需要修改所有访客。
将所有功能作为 virtual
方法放在 IShape
中的替代方案具有相反的优点和缺点:添加新功能需要修改所有现有形状,但添加新形状不会影响现有类。