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
中的替代方案具有相反的優點和缺點:新增新功能需要修改所有現有形狀,但新增新形狀不會影響現有類。