Відвідувач (шаблон проєктування)Відвідувач (Visitor) — шаблон проєктування, який дозволяє відділити певний алгоритм від елементів, на яких алгоритм має бути виконаний, таким чином можливо легко додати або ж змінити алгоритм без зміни елементів системи. Практичним результатом є можливість додавання нових операцій в існуючі структури об'єкта без зміни цих структур. Відвідувач дозволяє додавати нові віртуальні функції в родинні класи без зміни самих класів, натомість, один відвідувач створює клас, який реалізує всі відповідні спеціалізації віртуальної функції. Відвідувач приймає посилання на елемент й реалізується шляхом подвійної диспетчеризації. Проблема, яку вирішуєНад кожним об'єктом деякої структури виконується одна або більше операцій. Визначити нову операцію, не змінюючи класи об'єктів. Випадки застосуванняШаблон Відвідувач використовується, коли:
Опис шаблону Основним призначенням шаблону Відвідувач є введення абстрактної функціональності для сукупної ієрархічної структури об'єктів «елемент», а саме, шаблон Відвідувач дає змогу, не змінюючи класи елементів, додавати в них нові операції. Для цього вся обробна функціональність переноситься з самих класів елементів в ієрархію спадкування Відвідувача. Шаблон відвідувач дає змогу легко додавати нові операції — потрібно просто додати новий похідний від відвідувача клас. Однак, шаблон Відвідувач слід використовувати тільки в тому випадку, якщо підкласи елементів сукупної ієрархічної структури залишаються стабільними (незмінними). В іншому випадку, потрібно докласти значних зусиль на оновлення всієї ієрархії. Іноді наводяться заперечення з приводу використання шаблону Відвідувач, оскільки він розділяє дані та алгоритми, що суперечить концепції об'єктно-орієнтованого програмування. Однак успішний досвід застосування STL, де поділ даних і алгоритмів покладено в основу, доводить можливість використання Відвідувача.
РеалізаціяОсобливості шаблону
Частини шаблону
Взаємодія
Переваги
Недоліки
Приклади реалізаціїC++Приклад реалізації мовою С++
#include <iostream>
#include <string>
using namespace std;
struct Foo;
struct Bar;
struct Baz;
// Абстракція відвідувача
struct Visitor
{
virtual void visit(Foo &ref) = 0;
virtual void visit(Bar &ref) = 0;
virtual void visit(Baz &ref) = 0;
virtual ~Visitor() = default;
};
// Абстракція елемента
struct Element
{
virtual void accept(Visitor &v) = 0;
virtual ~Element() = default;
};
// Конкретні елементи
struct Foo : public Element
{
void accept(Visitor &v) override
{
v.visit(*this);
}
};
struct Bar : public Element
{
void accept(Visitor &v) override
{
v.visit(*this);
}
};
struct Baz : public Element
{
void accept(Visitor &v) override
{
v.visit(*this);
}
};
// Конкретний відвідувач
struct GetType : public Visitor
{
string value;
void visit(Foo &ref) override
{
value = "Foo";
}
void visit(Bar &ref) override
{
value = "Bar";
}
void visit(Baz &ref) override
{
value = "Baz";
}
};
void main()
{
Foo foo;
Bar bar;
Baz baz;
Element *elements[] = { &foo, &bar, &baz };
GetType visitor;
for (auto elem : elements)
{
elem->accept(visitor);
cout << visitor.value << endl;
}
}
С#Приклад реалізації мовою С#
namespace VisitorPattern
{
// Абстракція відвідувача
interface IGeometryVisitor
{
double Visit(Square square);
double Visit(Circle circle);
}
// Абстракція елемента
interface IGeometryElement
{
double Accept(IGeometryVisitor visitor);
}
// Конкретні елементи
class Square : IGeometryElement
{
public double Side { get; internal set; }
public double Accept(IGeometryVisitor visitor)
{
return visitor.Visit(this);
}
}
class Circle : IGeometryElement
{
public double Radius { get; internal set; }
public double Accept(IGeometryVisitor visitor)
{
return visitor.Visit(this);
}
}
// Конкретні відвідувачі
class GetAreaVisitor : IGeometryVisitor
{
public double Visit(Square square)
{
return square.Side * square.Side;
}
public double Visit(Circle circle)
{
return Math.PI * circle.Radius * circle.Radius;
}
}
class GetPerimeterVisitor : IGeometryVisitor
{
public double Visit(Square square)
{
return 4 * square.Side;
}
public double Visit(Circle circle)
{
return 2 * Math.PI * circle.Radius;
}
}
class Program
{
static void Main(string[] args)
{
IGeometryElement[] elements = new IGeometryElement[]
{
new Square
{
Side = 20,
},
new Circle
{
Radius = 15,
}
};
IGeometryVisitor geometryVisitor = new GetAreaVisitor();
foreach (var element in elements)
{
var area = element.Accept(geometryVisitor);
System.Console.WriteLine($"Area of {element.GetType().Name} equals {area}");
}
}
}
}
JavaПриклад реалізації мовою Java
public class Program {
public static void main (String [] args) {
Point p = new Point2d(1, 2);
Visitor v = new ChebyshevMetric();
double metric = p.accept(v);
System.out.println(metric);
}
}
interface Visitor {
public double visit (Point2d p);
public double visit (Point3d p);
}
abstract class Point {
public abstract double accept (Visitor v);
}
class Point2d extends Point {
public Point2d (double x, double y) {
this.x = x;
this.y = y;
}
public double accept (Visitor v) {
return v.visit(this);
}
private double x;
public double getX () { return x; }
private double y;
public double getY () { return y; }
}
class Point3d extends Point {
public Point3d (double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public double accept (Visitor v) {
return v.visit(this);
}
private double x;
public double getX () { return x; }
private double y;
public double getY () { return y; }
private double z;
public double getZ () { return z; }
}
// конкретні операції
class EuclidMetric implements Visitor {
public double visit (Point2d p) {
double x2 = p.getX() * p.getX();
double y2 = p.getY() * p.getY();
return Math.sqrt(x2 + y2);
}
public double visit (Point3d p) {
double x2 = p.getX() * p.getX();
double y2 = p.getY() * p.getY();
double z2 = p.getZ() * p.getZ();
return Math.sqrt(x2 + y2 + z2);
}
}
class ChebyshevMetric implements Visitor {
public double visit (Point2d p) {
double ax = Math.abs(p.getX());
double ay = Math.abs(p.getY());
return Math.max(ax, ay);
}
public double visit (Point3d p) {
double ax = Math.abs(p.getX());
double ay = Math.abs(p.getY());
double az = Math.abs(p.getZ());
return Math.max(Math.max(ax, ay), az);
}
}
class ManhattanMetric implements Visitor {
public double visit (Point2d p) {
double ax = Math.abs(p.getX());
double ay = Math.abs(p.getY());
return ax + ay;
}
public double visit (Point3d p) {
double ax = Math.abs(p.getX());
double ay = Math.abs(p.getY());
double az = Math.abs(p.getZ());
return ax + ay + az;
}
}
Див. такожПосилання
|