Читати книгу - "Занурення в патерни проектування, Олександр Швець"
Шрифт:
Інтервал:
Додати в закладку:
Шаблонний метод використовує спадкування, щоб розширювати частини алгоритму. Стратегія використовує делегування, щоб змінювати «на льоту» алгоритми, що виконуються. Шаблонний метод працює на рівні класів. Стратегія дозволяє змінювати логіку окремих об’єктів.
Також відомий як: VisitorВідвідувач — це поведінковий патерн проектування, що дає змогу додавати до програми нові операції, не змінюючи класи об’єктів, над якими ці операції можуть виконуватися.
ПроблемаВаша команда розробляє програму, що працює з геоданими у вигляді графа. Вузлами графа можуть бути як міста, так інші локації, такі, як пам’ятки, великі підприємства тощо. Кожен вузол має посилання на найближчі до нього вузли. Для кожного типу вузла існує свій власний клас, а кожен вузол представлений окремим об’єктом.
Експорт гео-вузлів до XML.
Ваше завдання — зробити експорт цього графа до XML. Справа була б легкою, якщо б ви могли редагувати класи вузлів. У цьому випадку можна було б додати метод експорту до кожного типу вузлів, а потім, перебираючи всі вузли графа, викликати цей метод для кожного вузла. Завдяки поліморфізму, рішення було б елегантним, оскільки ви могли б не прив’язуватися до конкретних класів вузлів.
Але, на жаль, змінити класи вузлів у вас не вийшло. Системний архітектор сказав, що код класів вузлів зараз дуже стабільний, і від нього багато що залежить, а тому він не хоче ризикувати, дозволяючи будь-кому чіпати цей код.
Код XML-експорту доведеться додати до всіх класів вузлів, а це дуже невигідно.
До того ж він сумнівався в тому, що експорт до XML взагалі є доречним в рамках цих класів. Їхнє основне завдання пов’язане з геоданими, а експорт виглядає в межах цих класів, як біла ворона.
Була ще одна причина заборони. Наступного тижня вам міг знадобитися експорт в який-небудь інший формат даних, а це призвело б до повторних змін в класах.
РішенняПатерн Відвідувач пропонує розмістити нову поведінку в окремому класі, замість того, щоб множити її відразу в декількох класах. Об’єкти, з якими повинна бути пов’язана поведінка, не виконуватимуть її самостійно. Замість цього ви будете передавати ці об’єкти до методів відвідувача.
Код поведінки, імовірно, повинен відрізнятися для об’єктів різних класів, тому й методів у відвідувача повинно бути декілька. Назви та принцип дії цих методів будуть подібними, а основна відмінність торкатиметься типу, що приймається в параметрах об’єкта, наприклад:
class ExportVisitor implements Visitor ismethod doForCity(City c) { ... }
method doForIndustry(Industry f) { ... }
method doForSightSeeing(SightSeeing ss) { ... }
// ...
Тут виникає запитання, яким чином ми будемо подавати вузли до об’єкта відвідувача. Оскільки усі методи відрізняються сигнатурою, використати поліморфізм при перебиранні вузлів не вийде. Доведеться перевіряти тип вузлів для того, щоб вибрати відповідний метод відвідувача.
foreach (Node node : graph)if (node instanceof City)
exportVisitor.doForCity((City) node);
if (node instanceof Industry)
exportVisitor.doForIndustry((Industry) node);
// ...
Тут не допоможе навіть механізм перевантаження методів (доступний у Java і C#). Якщо назвати всі методи однаково, то невизначеність реального типу вузла все одно не дасть викликати правильний метод. Механізм перевантаження весь час викликатиме метод відвідувача, відповідний типу Node, а не реального класу поданого вузла.
Але патерн Відвідувач вирішує і цю проблему, використовуючи механізм подвійної диспетчеризації. Замість того, щоб самим шукати потрібний метод, ми можемо доручити це об’єктам, які передаємо в параметрах відвідувачеві, а вони вже самостійно викличуть правильний метод відвідувача.
// Client codeforeach (Node node : graph)
node.accept(exportVisitor);
// City
class City is
method accept(Visitor v) is
v.doForCity(this);
// ...
// Industry
class Industry is
method accept(Visitor v) is
v.doForIndustry(this);
// ...
Як бачите, змінити класи вузлів все-таки доведеться. Проте ця проста зміна дозволить застосувати до об’єктів вузлів й інші поведінки, адже класи вузлів будуть прив’язані не до конкретного класу відвідувачів, а до їхнього загального інтерфейсу. Тому, якщо доведеться додати до програми нову поведінку, ви створите новий клас відвідувачів і будете передавати його до методів вузлів.
Аналогія з життяУ страхового агента приготовані поліси для різних видів організацій.
Уявіть собі страхового агента-початківця, який прагне отримати нових клієнтів. Він хаотично відвідує всі будинки навколо, пропонуючи свої послуги. Але для кожного типу будинків, які він відвідує, у нього є особлива пропозиція.
Прийшовши до будинку звичайної сім’ї, він пропонує оформити медичну страховку. Прийшовши до банку, він пропонує страховку на випадок пограбування. Прийшовши на фабрику, вінУвага!
Сайт зберігає кукі вашого браузера. Ви зможете в будь-який момент зробити закладку та продовжити читання книги «Занурення в патерни проектування, Олександр Швець», після закриття браузера.