Паттерн (шаблон) проектування Visitor (відвідувач)

Призначення патерну Visitor

  • Паттерн Visitor визначає операцію, що виконується кожному елементі з деякої структури. Дозволяє, не змінюючи класи цих об'єктів, додавати до них нові операції.
  • Є класичною технікою відновлення втраченої інформації про тип.
  • Паттерн Visitor дозволяє виконати потрібні дії залежно від двох типів об'єктів.
  • Надає механізм подвійної диспетчеризації.

Вирішувана проблема

Різні та непов'язані операції мають виконуватися над вузловими об'єктами деякої гетерогенної сукупної структури. Ви хочете уникнути "забруднення" класів цих вузлів такими операціями (тобто уникнути додавання відповідних методів до цих класів). І ви не хочете запитувати тип кожного вузла та здійснювати приведення покажчика до правильного типу, перш ніж виконати потрібну операцію.

Обговорення патерну Visitor

Основним призначенням патерна Visitor є введення абстрактної функціональності для сукупної ієрархічної структури об'єктів "елемент", а саме патерн Visitor дозволяє, не змінюючи класи Element, додавати в них нові операції. Для цього вся обробна функціональність переноситься із самих класів Element (ці класи стають "легковажними") в ієрархію спадкування Visitor.

При цьому патерн Visitor використовує техніку подвійної диспетчеризації. Зазвичай під час передачі запитів використовується "одинарна диспетчеризація" – те, яка операція буде виконано для обробки запиту, залежить від імені запиту та типу одержувача. У "подвійній диспетчеризації" викликана операція залежить від імені запиту та типів двох одержувачів (типу Visitor і типу елемента Element, що відвідувається).

Реалізуйте патернVisitor в такий спосіб. Створіть ієрархію класів Visitor, в абстрактному базовому класі якої для кожного підкласу Element сукупної структури визначається суто віртуальний метод visit(). Кожен метод visit() приймає один аргумент - покажчик чи посилання підклас Element .

Кожна нова операція, що додається, моделюється за допомогою конкретного підкласу Visitor. Підкласи Visitor реалізують visit() методи, оголошені базовому класі Visitor .

Додайте один чисто віртуальний метод accept() до базового класу ієрархії Element . Як параметр accept() приймає єдиний аргумент - покажчик чи посилання абстрактний базовий клас ієрархії Visitor .

Тепер "елементи" та "відвідувачі" готові. Якщо клієнту потрібно виконати якусь операцію, він створює екземпляр об'єкта відповідного підкласу Visitor і викликає accept() метод кожного об'єкта Element , передаючи екземпляр Visitor як параметр.

Під час виклику методу accept() шукається правильний підклас Element . Потім, при виклику visit() програмне управління передається правильному підкласу Visitor . Таким чином, подвійна диспетчеризація виходить як сума одинарних диспетчеризацій спочатку методу accept() , а потім методу visit() .

Паттерн Visitor дозволяє легко додавати нові операції – просто додати новий похідний від Visitor клас. Однак патерн Visitor слід використовувати тільки в тому випадку, якщо підкласи Element сукупної ієрархічної структури залишаються стабільними (незмінними). Інакше потрібно докласти значних зусиль на оновлення всієї ієрархії Visitor .

Іноді наводяться заперечення щодо використання патерна Visitor, оскільки він поділяє дані та алгоритми, що суперечитьконцепція об'єктно-орієнтованого програмування. Проте успішний досвід застосування STL, де поділ даних та алгоритмів покладено основою, доводить можливість використання патерна Visitor.

Структура патерну Visitor

UML-діаграма класів патерну Visitor

шаблон

При виклику поліморфного методу firstDispatch() об'єкта абстрактного типу First "відновлюється" конкретний тип цього об'єкта. Коли викликається поліморфний метод secondDispatch() об'єкта абстрактного типу Second, "відновлюється" його конкретний тип. Тепер може виконуватися функціональність, що відповідає цій парі типів.

шаблон

Приклад патерну Visitor

Паттерн Visitor визначає операцію, що виконується кожному елементі з певної структури без зміни класів цих об'єктів. Таксомоторна компанія використовує цей патерн у своїй роботі. Коли клієнт дзвонить до такої компанії, диспетчер відправляє до нього вільне таксі. Після того, як клієнт сідає в таксі, його доставляють до місця.

проектування

Використання патерну Visitor

Особливості патерну Visitor

  • Сукупна структура об'єктів Elements може визначатися за допомогою патерна Composite.
  • Для обходу Composite можна використовувати Iterator.
  • Паттерн Visitor демонструє класичний прийом відновлення інформації про втрачені типи, не вдаючись до приведення типів (dynamic cast).

Реалізація патерну Visitor

Реалізація патерну Visitor за кроками

  1. Додайте метод accept(Visitor) ієрархію "елемент".
  2. Створіть базовий клас Visitor та визначте методи visit() для кожного типу "елемента".
  3. Створіть похідні класи Visitor для кожної операції, що виконується над елементами.
  4. Клієнтстворює об'єкт Visitor і передає його в метод accept() .

Реалізація патерну Visitor: до та після

Інтерфейс для "операцій" визначається у базовому класі Color і реалізується у його підкласах.

Ієрархія Color визначає єдиний метод accept(), а методи count() та call() реалізовані у вигляді похідних класів Visitor. При виклику методу accept() об'єкта Color відбувається перша диспетчеризація, а виклику методу visit() об'єкта Visitor – друга. Після цього на основі типів обох об'єктів можуть виконуватись всі необхідні дії.