Паттерн
Завантажити презентаціюПрезентація по слайдам:
Відвідувач — це поведінковий патерн проектування, що дає змогу додавати до програми нові операції, не змінюючи класи об’єктів, над якими ці операції можуть виконуватися. Відвідувач (Visitor) дозволяє відділити певний алгоритм від елементів, на яких алгоритм має бути виконаний, таким чином ми можемо легко додати або ж змінити алгоритм без змін до елементів системи. Як на мене, це і є однією із найбільш помітних переваг цього патерну. Суть патерна:
Представляє операцію, яка має бути виконана на елементах структури об’єкта. Відвідувач дозволяє визначити нову операцію без зміни класів елементів-операндів. Призначення:
Якщо вам потрібно виконати якусь операцію над усіма елементами складної структури об’єктів, наприклад, деревом. Відвідувач дозволяє застосовувати одну і ту саму операцію до об’єктів різних класів. Якщо над об’єктами складної структури об’єктів потрібно виконувати деякі не пов’язані між собою операції, але ви не хочете «засмічувати» класи такими операціями. Відвідувач дозволяє витягти споріднені операції з класів, що складають структуру об’єктів, помістивши їх до одного класу-відвідувача. Якщо структура об’єктів використовується в декількох програмах, то патерн дозволить кожній програмі мати тільки потрібні в ній операції. Якщо нова поведінка має сенс тільки для деяких класів з існуючої ієрархії. Відвідувач дозволяє визначити поведінку тільки для цих класів, залишивши її порожньою для всіх інших. Застосування:
Ваша команда розробляє програму, що працює з геоданими у вигляді графа. Вузлами графа можуть бути як міста, так інші локації, такі, як пам’ятки, великі підприємства тощо. Кожен вузол має посилання на найближчі до нього вузли. Для кожного типу вузла існує свій власний клас, а кожен вузол представлений окремим об’єктом. Експорт гео-вузлів до XML Проблема:
Ваше завдання — зробити експорт цього графа до XML. Справа була б легкою, якщо б ви могли редагувати класи вузлів. У цьому випадку можна було б додати метод експорту до кожного типу вузлів, а потім, перебираючи всі вузли графа, викликати цей метод для кожного вузла. Завдяки поліморфізму, рішення було б елегантним, оскільки ви могли б не прив’язуватися до конкретних класів вузлів. Але, на жаль, змінити класи вузлів у вас не вийшло. Системний архітектор сказав, що код класів вузлів зараз дуже стабільний, і від нього багато що залежить, а тому він не хоче ризикувати, дозволяючи будь-кому чіпати цей код. Код XML-експорту доведеться додати до всіх класів вузлів, а це дуже невигідно
До того ж він сумнівався в тому, що експорт до XML взагалі є доречним в рамках цих класів. Їхнє основне завдання пов’язане з геоданими, а експорт виглядає в межах цих класів, як біла ворона. Була ще одна причина заборони. Наступного тижня вам міг знадобитися експорт в який-небудь інший формат даних, а це призвело б до повторних змін в класах.
Патерн Відвідувач пропонує розмістити нову поведінку в окремому класі, замість того, щоб множити її відразу в декількох класах. Об’єкти, з якими повинна бути пов’язана поведінка, не виконуватимуть її самостійно. Замість цього ви будете передавати ці об’єкти до методів відвідувача. Код поведінки, імовірно, повинен відрізнятися для об’єктів різних класів, тому й методів у відвідувача повинно бути декілька. Рішення:
У страхового агента приготовані поліси для різних видів організацій Уявіть собі страхового агента-початківця, який прагне отримати нових клієнтів. Він хаотично відвідує всі будинки навколо, пропонуючи свої послуги. Але для кожного типубудинків, які він відвідує, у нього є особлива пропозиція. Прийшовши до будинку звичайної сім’ї, він пропонує оформити медичну страховку. Прийшовши до банку, він пропонує страховку на випадок пограбування. Прийшовши на фабрику, він пропонує страхування підприємства на випадок пожежі чи повені. Аналогія із життя:
Структура: 1. Відвідувач описує спільний для всіх типів відвідувачів інтерфейс. Він оголошує набір методів, що відрізняються типом вхідного параметра. Кожному класу конкретних елементів повинен підходити свій метод. В мовах, які підтримують перевантаження методів, ці методи можуть мати однакові імена, але типи їхніх параметрів повинні відрізнятися. 2. Конкретні відвідувачі реалізують якусь особливу поведінку для всіх типів елементів, які можна подати через методи інтерфейсу відвідувача. 3. Елемент описує метод прийому відвідувача. Цей метод повинен мати лише один параметр, оголошений з типом загального інтерфейсу відвідувачів. 4. Конкретні елементи реалізують методи приймання відвідувача. Мета цього методу — викликати той метод відвідування, який відповідає типу цього елемента. Так відвідувач дізнається, з яким типом елементу він працює. 5. Клієнтом зазвичай виступає колекція або складний складовий об’єкт, наприклад, дерево Компонувальника. Здебільшого, клієнт не прив’язаний до конкретних класів елементів, працюючи з ними через загальний інтерфейс елементів.
У цьому прикладі Посередник допомагає позбутися залежностей між класами різних елементів користувацього інтерфейсу: кнопками, чекбоксами й написами. Приклад організації експорту об’єктів XML через окремий клас-відвідувач #Псевдокод:
1. Створіть інтерфейс відвідувача й оголосіть у ньому методи «відвідування» для кожного класу елемента, який існує в програмі. 2. Опишіть інтерфейс елементів. Якщо ви працюєте з уже існуючими класами, оголосіть абстрактний метод прийняття відвідувачів у базовому класі ієрархії елементів. 3. Реалізуйте методи прийняття в усіх конкретних елементах. Вони повинні переадресовувати виклики тому методу відвідувача, в якому тип параметра збігається з поточним класом елемента. Кроки реалізації:
4. Ієрархія елементів повинна знати тільки про загальний інтерфейс відвідувачів. З іншого боку, відвідувачі знатимуть про всі класи елементів. 5. Для кожної нової поведінки створіть свій власний конкретний клас. Пристосуйте цю поведінку для роботи з усіма наявними типами елементів, реалізувавши всі методи інтерфейсу відвідувачів. Ви можете зіткнутися з ситуацією, коли відвідувачу потрібен доступ до приватних полів елементів. У цьому випадку ви можете або розкрити доступ до цих полів, порушивши інкапсуляцію елементів, або зробити клас відвідувача вкладеним в клас елемента, якщо вам пощастило писати мовою, яка підтримує механізм вкладених класів. 6. Клієнт створюватиме об’єкти відвідувачів, а потім передаватиме їх елементам через метод прийняття.
Переваги та недоліки: ✓ Спрощує додавання операцій, працюючих зі складними структурами об’єктів. ✓ Об’єднує споріднені операції в одному класі. ✓ Відвідувач може накопичувати стан при обході структури елементів. ☓ Патерн невиправданий, якщо ієрархія елементів часто змінюється. ☓ Може призвести до порушення інкапсуляції елементів.
Реалізація мовою с#: Уявімо собі, що ви, нарешті, спромоглися створити свою власну компанію і, оскільки вона пристойного розміру, ви вирішили орендувати для неї цілу будівлю. У нас держава дуже хороша і дбає про підприємства. А щоб у підприємств усе відповідало вимогам, постійно висилаються різноманітні перевірки. Причому правила, за якими перевіряють ваше підприємство, постійно змінюються. Найближчим часом вам слід буде прийняти багато відвідувачів (visitors), таких як електрик (electrician), сантехнік (plumber), податківець і так далі… Усі вони будуть перевіряти вашу будівлю вздовж і впоперек, проходячи від поверха до поверха, від кімнати до кімнати. Я підозрюю, що якась певна схема класів у вас у голові вже з’явилася. Якщо так, то у мене є наступне питання: де має жити логіка певної перевірки будівлі? Чи має будівля знати, як перевіряти електричні щитки, чи це має знати електрик, або чи має знати кімната, як перевірити вимикачі, чи це так само робота електрика? Звичайно, що електрик, який і є відвідувачем, інкапсулює логіку перевірки певних елементів (elements) вашої будівлі.
Отже, як і було згадано вище, інкапсульована логіка живе у конкретному відвідувачі. Ця логіка може бути застосована до елементів системи. Основу цього дизайн-патерну можна вибудувати на двох інтерфейсах. Ось вони: Уривок коду(IVisitor та IElement): У нашому прикладі ElectricitySystemValidator є однією із конкретних реалізацій інтерфейсу IVisitor, яка може виглядати як наведений нижче код:
Про що говорить нам цей клас? Ми зауважили, що у відповідності до інтерфейсу IVisitor в одному відвідувачі є три методи, кожен із яких описує логіку перевірки для одного із елементів. Виходячи із цього, ми можемо з чистою совістю проводити нашого відвідувача із поверху на поверх та з кімнати до кімнати. Клас PlumbingSystemValidator схожий на ElectricitySystemValidator, але в своїй логіці бере до уваги вік будівлі, щоб приблизно оцінити, наскільки справною є сантехнічна частина. Що ще цікаво про цей клас, так це те, що він нічого не робить у кімнатах. Звісно, якщо ваше підприємство є якимось хімзаводом, сантехніку доведеться пройтися по всіх кімнатах. До цього часу вже стало зрозуміло, що структура будівлі обхідна. Все починається із будівлі (OfficeBuilding), яка має поверхи (Floors), і кожен із поверхів може мати багато кімнат. Глянемо на імплементацію поверху.
Як можна побачити, цей клас мітить метод Accept, який вимагається інтерфейсом і який приймає відвідувача. Всередині цього методу ми виконуємо наш алгорим і, якщо треба, передаємо нашого відвідувача «по колу». Як бачимо, ніякі технічні перевірки не виконуються напряму у цьому класі, тому ми можемо бути певні, що якщо у майбутньому слід буде змінити спосіб перевірки електросистеми у кімнаті, то це буде зроблено у відвідувачі без будьяких впливів на клас кімнати. OfficeBuiling є досить подібним класом, хіба що має багато інших додаткових властивостей. Room взагалі є простим класом, який не агрегує чи компонує інших елементів. Глянемо на код використання дизайн-патерну:
Уривок коду(Маємо будівлю із 2-ма поверхами, на кожному є по 3 кімнати. Запускаємо у будівлю електрика і сантехніка як відвідувачів):
Схожі презентації
Категорії