Возможные реализации для решения конкретных задач
Visitor
Посетитель с приведением типа между базовыми классами
Реализация посетителя с приведением типа между базовыми классами использует пустой абстрактный класс посетителя, который дополняется посетителями конкретных типов при помощи реализации идиомы MixIn.
Подход может быть использован, в случае, если требуется создавать посетители с сильно разнящимся поведением относительно посещаемых типов, когда мы не хотим размещать разную логику обработки сущностей рядом в одном классе.
#include <iostream>
#include <vector>
#include <memory>
using namespace std;class AbstractVisitor
{
public:
virtual ~AbstractVisitor() = default;
};template <typename T>
class Visitor
{
public:
virtual ~Visitor() = default;
virtual void visit(const T&) const = 0;
};class Shape
{
public:
Shape() = default;
virtual ~Shape() = default;
virtual void accept(const AbstractVisitor&) const = 0;
};class Circle : public Shape
{
private:
double radius;
public:
Circle(double radius) : radius(radius) {}
void accept(const AbstractVisitor& v) const override
{
auto cv = dynamic_cast<const Visitor<Circle>*>(&v);
if (cv)
cv->visit(*this);
}
};class Square : public Shape
{
private:
double side;
public:
Square(double side) : side(side) {}
void accept(const AbstractVisitor& v) const override
{
auto cv = dynamic_cast<const Visitor<Square>*>(&v);
if (cv)
cv->visit(*this);
}
};class DrawCircle : public Visitor<Circle>
{
void visit(const Circle& circle) const override
{
cout << "Circle" << endl;
}
};class DrawSquare : public Visitor<Square>
{
void visit(const Square& circle) const override
{
cout << "Square" << endl;
}
};class Figure : public Shape
{
using Shapes = vector<shared_ptr<Shape>>;
private:
Shapes shapes;
public:
Figure(initializer_list<shared_ptr<Shape>> list)
{
for (auto&& elem : list)
shapes.emplace_back(elem);
}
void accept(const AbstractVisitor& visitor) const override
{
for (auto& elem : shapes)
elem->accept(visitor);
}
};class Draw : public AbstractVisitor, public DrawCircle, public DrawSquare {};int main()
{
shared_ptr<Shape> figure = make_shared<Figure>(
initializer_list<shared_ptr<Shape>>(
{ make_shared<Circle>(1), make_shared<Square>(2) }
)
);
figure->accept(Draw{});
}Посетитель с использованием шаблона variant ("безопасный" union)
#include <iostream>
#include <vector>
#include <variant>
using namespace std;class Circle {};class Square {};using Shape = std::variant<Circle, Square>;class Formation
{
public:
static vector<Shape> initialization(initializer_list<Shape> list)
{
vector<Shape> vec;
for (auto&& elem : list)
vec.emplace_back(elem);
return vec;
}
};class Draw
{
public:
void operator ()(const Circle&) const
{
cout << "Circle" << endl;
}
void operator ()(const Square&) const
{
cout << "Square" << endl;
}
};int main()
{
using Shapes = vector<Shape>;
Shapes fiqure = Formation::initialization({ Circle{}, Square{} });
for (const auto& elem : fiqure)
std::visit(Draw{}, elem);
}Шаблонный посетитель (Template visitor) с использованием паттерна CRTP (Curiously Recurring Template Pattern)
Реализация шаблонного посетителя прибегает к вариативным шаблонам классов для определения типа посетителя, который посещает заданные классы. За счёт использования CRTP можно избавиться от реализации метода accept в каждой сущности, перенеся ответственность за это на класс Visitable.
# include <iostream>
# include <memory>
# include <initializer_list>
# include <vector>
using namespace std;template <typename... Types>
class Visitor;
template <typename Type>
class Visitor<Type>
{
public:
virtual void visit(Type& t) = 0;
};
template <typename Type, typename... Types>
class Visitor<Type, Types...> : public Visitor<Types...>
{
public:
using Visitor<Types...>::visit;
virtual void visit(Type& t) = 0;
};using ShapeVisitor = Visitor<class Figure, class Camera>;class Point {};class Shape
{
public:
Shape(const Point& pnt) : point(pnt) {}
virtual ~Shape() = default;
const Point& getPoint() const
{
return point;
}
void setPoint(const Point& pnt)
{
point = pnt;
}
virtual void accept(shared_ptr<ShapeVisitor> v) = 0;
private:
Point point;
};template <typename Derived>
class Visitable : public Shape
{
public:
using Shape::Shape;
void accept(shared_ptr<ShapeVisitor> v) override
{
v->visit(*static_cast<Derived*>(this));
}
};class Figure : public Visitable<Figure>
{
using Visitable<Figure>::Visitable;
};class Camera : public Visitable<Camera>
{
using Visitable<Camera>::Visitable;
};class Composite : public Shape
{
using Shapes = vector<shared_ptr<Shape>>;
private:
Shapes shapes{};
public:
Composite(initializer_list<shared_ptr<Shape>> list) : Shape(Point{})
{
for (auto&& elem : list)
shapes.emplace_back(elem);
}
void accept(shared_ptr<ShapeVisitor> visitor) override
{
for (auto& elem : shapes)
elem->accept(visitor);
}
};class DrawVisitor : public ShapeVisitor
{
public:
void visit(Figure& fig) override
{
cout << "Draws a figure;" << endl;
}
void visit(Camera& fig) override
{
cout << "Draws a camera;" << endl;
}
};int main()
{
Point p;
shared_ptr<Composite> figure = make_shared<Composite>(
initializer_list<shared_ptr<Shape>>(
{ make_shared<Figure>(p), make_shared<Camera>(p), make_shared<Figure>(p) }
)
);
shared_ptr<ShapeVisitor> visitor = make_shared<DrawVisitor>();
figure->accept(visitor);
}Last updated
Was this helpful?