Відвідувач на Go
Відвідувач — це поведінковий патерн, який дозволяє додати нову операцію для цілої ієрархії класів, не змінюючи код цих класів.
Детальніше про те, чому Відвідувач не можна замінити звичайним перевантаженням методів читайте в статті Відвідувач і Double Dispatch.
Концептуальний приклад
Патерн Відвідувач дає змогу вам додавати поведінку в структуру без її зміни. Уявімо, що ви розробник бібліотеки, яка містить структури різних фігур:
- Квадрат
- Коло
- Трикутник
Структури кожної з вищеназваних фігур реалізують спільний інтерфейс фігури.
Як тільки співробітники у вашій компанії почали використовувати вашу чудову бібліотеку, вас засипали проханнями додати той чи інший функціонал. Розглянемо один з найпростіших варіантів: команда попросила вас додати в структуру функцію getArea
, що повертає площу фігури.
Є багато рішень цієї проблеми.
Перше, що спадає на думку — додати метод getArea
безпосередньо в інтерфейс фігури, і потім реалізувати його у кожній структурі. Це здається найбільш правильним рішенням, але у нього є свої мінуси. Як розробник бібліотеки, ви ризикуєте зламати ваш дорогоцінний код щоразу, коли додаєте нову поведінку. Незважаючи на це, ви хочете дати іншим командам можливість якимось чином розширювати вашу бібліотеку.
Другий варіант — команда, яка запитує додавання нової функції, може сама реалізувати її в своєму проекті. Однак, це не завжди є можливим, оскільки новий функціонал може покладатися на прихований код.
Третій можливий варіант — вирішити вищезгадану проблему завдяки використанню патерна Відвідувач. Спершу ми визначаємо інтерфейс користувача в такий спосіб:
Функції visitForSquare(square)
, visitForCircle(circle)
, visitForTriangle(triangle)
дозволять нам додавати функціонал для квадратів, кіл і трикутників відповідно.
Не розумієте, чому ми не можемо залишити лише один метод visit(shape)
в інтерфейсі користувача? Це неможливо через те, що мова Go не підтримує перевантаження методів, тому ви не можете мати методи з однаковими іменами, але різними параметрами.
Другий, не менш важливий, етап — додавання методу accept
в інтерфейс фігури.
Всі структури фігур мусять визначати цей метод схожим способом:
Стривайте, хіба я трохи раніше не заявляв, що не хочу змінювати наявні структури фігур? На жаль, під час використання патерну Відвідувача нам доведеться вносити зміни в структури, але лише один раз.
У разі додавання іншого функціоналу, наприклад getNumSides
або getMiddleCoordinates
, ми будемо використовувати все той же метод accept(v visitor)
без нових змін у структурах фігур.
В кінцевому підсумку, структури потрібно змінити лише один раз, і всі майбутні запити нового функціоналу можна буде реалізувати завдяки функції accept(v visitor)
. Якщо команда дасть запит на поведінку getArea
, ми можемо просто визначити явну реалізацію інтерфейсу користувача і прописати логіку обчислення площі в цій конкретній імплементації.