Відвідувач (Visitor) — шаблон проєктування, який дозволяє відділити певний алгоритм від елементів, на яких алгоритм має бути виконаний, таким чином можливо легко додати або ж змінити алгоритм без зміни елементів системи. Практичним результатом є можливість додавання нових операцій в існуючі структури об'єкта без зміни цих структур.
Відвідувач дозволяє додавати нові віртуальні функції в родинні класи без зміни самих класів, натомість, один відвідувач створює клас, який реалізує всі відповідні спеціалізації віртуальної функції. Відвідувач приймає посилання на елемент й реалізується шляхом подвійної диспетчеризації.
Проблема, яку вирішує
Над кожним об'єктом деякої структури виконується одна або більше операцій. Визначити нову операцію, не змінюючи класи об'єктів.
Випадки застосування
Шаблон Відвідувач використовується, коли:
- існує багато об'єктів різних класів з різними інтерфейсами, і потрібно виконати операцію над цими об'єктами, залежно від їх типу.
- потрібно виконувати над об'єктами багато не пов'язаних між собою операцій, але не хочеться "забруднювати" цими операціями класи об'єктів. При цьому, якщо ці об'єкти використовуються у декількох проєктах, з'являється можливість включати операції лише в ті проєкти, де вони необхідні.
- класи, які визначають структуру даних, змінюються рідко, але є потреба часто визначати нові операції над цією структурою. Якщо ж класи, які визначають структуру даних, змінюються часто, краще визначити операції в цих класах, адже при зміні інтерфейсів у класі даних необхідно змінювати і класи, які реалізують операції над цією структурою.
- треба визначити операцію над деякою структурою, не змінюючи клас цієї структури.
Опис шаблону
Основним призначенням шаблону Відвідувач є введення абстрактної функціональності для сукупної ієрархічної структури об'єктів «елемент», а саме, шаблон Відвідувач дає змогу, не змінюючи класи елементів, додавати в них нові операції. Для цього вся обробна функціональність переноситься з самих класів елементів в ієрархію спадкування Відвідувача.
Шаблон відвідувач дає змогу легко додавати нові операції — потрібно просто додати новий похідний від відвідувача клас. Однак, шаблон Відвідувач слід використовувати тільки в тому випадку, якщо підкласи елементів сукупної ієрархічної структури залишаються стабільними (незмінними). В іншому випадку, потрібно докласти значних зусиль на оновлення всієї ієрархії.
Іноді наводяться заперечення з приводу використання шаблону Відвідувач, оскільки він розділяє дані та алгоритми, що суперечить концепції об'єктно-орієнтованого програмування. Однак успішний досвід застосування STL, де поділ даних і алгоритмів покладено в основу, доводить можливість використання Відвідувача.
Реалізація
Особливості шаблону
- Сукупна структура об'єктів елементу може визначатися за допомогою патерну Компонувальник (Composite).
- Для обходу може використовуватися Ітератор (Iterator).
- Шаблон Відвідувач демонструє класичний прийом відновлення інформації про втрачені типи, не вдаючись до понижуючого приведення типів (динамічне приведення).
- Використовує механізм багаторазової диспетчеризації.
Частини шаблону
- Відвідувач (зазвичай, абстрактний клас чи інтерфейс)
- визначає дію над кожним класом конкретних елементів. Ім'я та сигнатура операції мають визначати конкретний клас даних, елемент якого треба відвідати. Це дає можливість відвідувачу доступатися до елементів через інтерфейс конкретного класу.
- Конкретний відвідувач (конкретний клас, що наслідує Відвідувач)
- реалізує чи перевизначає операції, визначені в базовому класі. Містить алгоритми, які виконуватимуться над об'єктами відповідного класу. Також даний клас утримує локальний стан алгоритмів (цей стан, зазвичай, утримує проміжні результати під час обходу структури, та ін.).
- Елемент (зазвичай, абстрактний клас чи інтерфейс)
- визначає операцію, яка приймає об'єкт відвідувача як аргумент.
- Конкретний елемент (конкретний клас, що наслідує Елемент)
- визначає операцію, що приймає об'єкт відвідувача як аргумент.
- Структура елементів (клас, що реалізує структуру елементів)
- може перераховувати елементи, які містить.
- надає високорівневий інтерфейс, що дозволяє відвідувачу виконувати елементи.
- може бути Компонувальником чи колекцією (як список або черга).
Взаємодія
- Клієнт, що використовує Відвідувача, має створити об'єкт Конкретного відвідувача та обійти потрібні елементи, відвідуючи кожен з них цим об'єктом.
- Коли елемент відвідується, він викликає операцію Відвідувача, що відповідає класу елементу. Елемент передає себе як аргумент в цю операцію щоб дозволити Відвідувачу доступ до його стану, якщо це необхідно.
Переваги
- спрощується додавання нових операцій;
- об'єднання споріднених операції в класі Visitor;
- клас Visitor може запам’ятовувати в собі якийсь стан під час обходу контейнера.
Недоліки
- важке додавання нових класів, оскільки потрібно оновлювати ієрархію відвідувачів.
Приклади реалізації
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
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; } }
Див. також
Посилання
- Visitor Design Pattern [ 18 квітня 2012 у Wayback Machine.]
- ]
- Visitor Pattern using reflection(java) [ 3 березня 2008 у Wayback Machine.]
- Design Patterns: Elements of Reusable Object-Oriented Software [ 9 листопада 2012 у Wayback Machine.]
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Vidviduvach Visitor shablon proyektuvannya yakij dozvolyaye viddiliti pevnij algoritm vid elementiv na yakih algoritm maye buti vikonanij takim chinom mozhlivo legko dodati abo zh zminiti algoritm bez zmini elementiv sistemi Praktichnim rezultatom ye mozhlivist dodavannya novih operacij v isnuyuchi strukturi ob yekta bez zmini cih struktur Vidviduvach dozvolyaye dodavati novi virtualni funkciyi v rodinni klasi bez zmini samih klasiv natomist odin vidviduvach stvoryuye klas yakij realizuye vsi vidpovidni specializaciyi virtualnoyi funkciyi Vidviduvach prijmaye posilannya na element j realizuyetsya shlyahom podvijnoyi dispetcherizaciyi Problema yaku virishuyeNad kozhnim ob yektom deyakoyi strukturi vikonuyetsya odna abo bilshe operacij Viznachiti novu operaciyu ne zminyuyuchi klasi ob yektiv Vipadki zastosuvannyaShablon Vidviduvach vikoristovuyetsya koli isnuye bagato ob yektiv riznih klasiv z riznimi interfejsami i potribno vikonati operaciyu nad cimi ob yektami zalezhno vid yih tipu potribno vikonuvati nad ob yektami bagato ne pov yazanih mizh soboyu operacij ale ne hochetsya zabrudnyuvati cimi operaciyami klasi ob yektiv Pri comu yaksho ci ob yekti vikoristovuyutsya u dekilkoh proyektah z yavlyayetsya mozhlivist vklyuchati operaciyi lishe v ti proyekti de voni neobhidni klasi yaki viznachayut strukturu danih zminyuyutsya ridko ale ye potreba chasto viznachati novi operaciyi nad ciyeyu strukturoyu Yaksho zh klasi yaki viznachayut strukturu danih zminyuyutsya chasto krashe viznachiti operaciyi v cih klasah adzhe pri zmini interfejsiv u klasi danih neobhidno zminyuvati i klasi yaki realizuyut operaciyi nad ciyeyu strukturoyu treba viznachiti operaciyu nad deyakoyu strukturoyu ne zminyuyuchi klas ciyeyi strukturi Opis shablonu Osnovnim priznachennyam shablonu Vidviduvach ye vvedennya abstraktnoyi funkcionalnosti dlya sukupnoyi iyerarhichnoyi strukturi ob yektiv element a same shablon Vidviduvach daye zmogu ne zminyuyuchi klasi elementiv dodavati v nih novi operaciyi Dlya cogo vsya obrobna funkcionalnist perenositsya z samih klasiv elementiv v iyerarhiyu spadkuvannya Vidviduvacha Shablon vidviduvach daye zmogu legko dodavati novi operaciyi potribno prosto dodati novij pohidnij vid vidviduvacha klas Odnak shablon Vidviduvach slid vikoristovuvati tilki v tomu vipadku yaksho pidklasi elementiv sukupnoyi iyerarhichnoyi strukturi zalishayutsya stabilnimi nezminnimi V inshomu vipadku potribno doklasti znachnih zusil na onovlennya vsiyeyi iyerarhiyi Inodi navodyatsya zaperechennya z privodu vikoristannya shablonu Vidviduvach oskilki vin rozdilyaye dani ta algoritmi sho superechit koncepciyi ob yektno oriyentovanogo programuvannya Odnak uspishnij dosvid zastosuvannya STL de podil danih i algoritmiv pokladeno v osnovu dovodit mozhlivist vikoristannya Vidviduvacha RealizaciyaOsoblivosti shablonu Sukupna struktura ob yektiv elementu mozhe viznachatisya za dopomogoyu paternu Komponuvalnik Composite Dlya obhodu mozhe vikoristovuvatisya Iterator Iterator Shablon Vidviduvach demonstruye klasichnij prijom vidnovlennya informaciyi pro vtracheni tipi ne vdayuchis do ponizhuyuchogo privedennya tipiv dinamichne privedennya Vikoristovuye mehanizm bagatorazovoyi dispetcherizaciyi Chastini shablonu Vidviduvach zazvichaj abstraktnij klas chi interfejs viznachaye diyu nad kozhnim klasom konkretnih elementiv Im ya ta signatura operaciyi mayut viznachati konkretnij klas danih element yakogo treba vidvidati Ce daye mozhlivist vidviduvachu dostupatisya do elementiv cherez interfejs konkretnogo klasu Konkretnij vidviduvach konkretnij klas sho nasliduye Vidviduvach realizuye chi pereviznachaye operaciyi viznacheni v bazovomu klasi Mistit algoritmi yaki vikonuvatimutsya nad ob yektami vidpovidnogo klasu Takozh danij klas utrimuye lokalnij stan algoritmiv cej stan zazvichaj utrimuye promizhni rezultati pid chas obhodu strukturi ta in Element zazvichaj abstraktnij klas chi interfejs viznachaye operaciyu yaka prijmaye ob yekt vidviduvacha yak argument Konkretnij element konkretnij klas sho nasliduye Element viznachaye operaciyu sho prijmaye ob yekt vidviduvacha yak argument Struktura elementiv klas sho realizuye strukturu elementiv mozhe pererahovuvati elementi yaki mistit nadaye visokorivnevij interfejs sho dozvolyaye vidviduvachu vikonuvati elementi mozhe buti Komponuvalnikom chi kolekciyeyu yak spisok abo cherga Vzayemodiya Kliyent sho vikoristovuye Vidviduvacha maye stvoriti ob yekt Konkretnogo vidviduvacha ta obijti potribni elementi vidviduyuchi kozhen z nih cim ob yektom Koli element vidviduyetsya vin viklikaye operaciyu Vidviduvacha sho vidpovidaye klasu elementu Element peredaye sebe yak argument v cyu operaciyu shob dozvoliti Vidviduvachu dostup do jogo stanu yaksho ce neobhidno Perevagisproshuyetsya dodavannya novih operacij ob yednannya sporidnenih operaciyi v klasi Visitor klas Visitor mozhe zapam yatovuvati v sobi yakijs stan pid chas obhodu kontejnera Nedolikivazhke dodavannya novih klasiv oskilki potribno onovlyuvati iyerarhiyu vidviduvachiv Prikladi realizaciyiC Priklad realizaciyi movoyu S include lt iostream gt include lt string gt using namespace std struct Foo struct Bar struct Baz Abstrakciya vidviduvacha struct Visitor virtual void visit Foo amp ref 0 virtual void visit Bar amp ref 0 virtual void visit Baz amp ref 0 virtual Visitor default Abstrakciya elementa struct Element virtual void accept Visitor amp v 0 virtual Element default Konkretni elementi struct Foo public Element void accept Visitor amp v override v visit this struct Bar public Element void accept Visitor amp v override v visit this struct Baz public Element void accept Visitor amp v override v visit this Konkretnij vidviduvach struct GetType public Visitor string value void visit Foo amp ref override value Foo void visit Bar amp ref override value Bar void visit Baz amp ref override value Baz void main Foo foo Bar bar Baz baz Element elements amp foo amp bar amp baz GetType visitor for auto elem elements elem gt accept visitor cout lt lt visitor value lt lt endl S Priklad realizaciyi movoyu S namespace VisitorPattern Abstrakciya vidviduvacha interface IGeometryVisitor double Visit Square square double Visit Circle circle Abstrakciya elementa interface IGeometryElement double Accept IGeometryVisitor visitor Konkretni elementi 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 Konkretni vidviduvachi 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 Priklad realizaciyi movoyu 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 konkretni operaciyi 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 Div takozhOdnorazovij vidviduvach shablon povedinki PosilannyaVisitor Design Pattern 18 kvitnya 2012 u Wayback Machine Visitor Pattern using reflection java 3 bereznya 2008 u Wayback Machine Design Patterns Elements of Reusable Object Oriented Software 9 listopada 2012 u Wayback Machine