Шаблонний метод
Суть патерна
Шаблонний метод — це поведінковий патерн проектування, який визначає кістяк алгоритму, перекладаючи відповідальність за деякі його кроки на підкласи. Патерн дозволяє підкласам перевизначати кроки алгоритму, не змінюючи його загальної структури.
Проблема
Ви пишете програму для дата-майнінгу в офісних документах. Користувачі завантажуватимуть до неї документи різних форматів (PDF, DOC, CSV), а програма повинна видобути з них корисну інформацію.
У першій версії ви обмежилися обробкою тільки DOC файлів. У наступній версії додали підтримку CSV. А через місяць «прикрутили» роботу з PDF документами.
В якийсь момент ви помітили, що код усіх трьох класів обробки документів хоч і відрізняється в частині роботи з файлами, але містить досить багато спільного в частині самого видобування даних. Було б добре позбутися від повторної реалізації алгоритму видобування даних у кожному з класів.
До того ж інший код, який працює з об’єктами цих класів, наповнений умовами, що перевіряють тип обробника перед початком роботи. Весь цей код можна спростити, якщо злити всі три класи в одне ціле або звести їх до загального інтерфейсу.
Рішення
Патерн Шаблонний метод пропонує розбити алгоритм на послідовність кроків, описати ці кроки в окремих методах і викликати їх в одному шаблонному методі один за одним.
Це дозволить підкласам перевизначити деякі кроки алгоритму, залишаючи без змін його структуру та інші кроки, які для цього підкласу не є важливими.
У нашому прикладі з дата-майнінгом ми можемо створити загальний базовий клас для всіх трьох алгоритмів. Цей клас складатиметься з шаблонного методу, який послідовно викликає кроки розбору документів.
Для початку кроки шаблонного методу можна зробити абстрактними. З цієї причини усі підкласи повинні будуть реалізувати кожен з кроків по-своєму. В нашому випадку всі підкласи вже містять реалізацію кожного з кроків, тому додатково нічого робити не потрібно.
Справді важливим є наступний етап. Тепер ми можемо визначити спільну поведінку для всіх трьох класів і винести її до суперкласу. У нашому прикладі кроки відкривання та закривання документів відрізнятимуться для всіх підкласів, тому залишаться абстрактними. З іншого боку, код обробки даних, однаковий для всіх типів документів, переїде до базового класу.
Як бачите, у нас з’явилося два типа кроків: абстрактні, що кожен підклас обов’язково має реалізувати, а також кроки з типовою реалізацією, які можна перевизначити в підкласах, але це не обов’язково.
Але є ще й третій тип кроків — хуки. Це опціональні кроки, які виглядають як звичайні методи, але взагалі не містять коду. Шаблонний метод залишиться робочим, навіть якщо жоден підклас не перевизначить такий хук. Підсумовуючи сказане, хук дає підкласам додаткові точки «вклинювання» в хід шаблонного методу.
Аналогія з життя
Під час будівництва типових будинків будівельники використовують підхід, схожий на шаблонний метод. У них є основний архітектурний проект, в якому розписані кроки будівництва: заливка фундаменту, витягування стін, покриття даху, встановлення вікон тощо.
Але, незважаючи на стандартизацію кожного етапу, будівельники можуть робити невеликі зміни на кожному з етапів, щоб зробити будинок трішечки не схожим на інші.
Структура
-
Абстрактний клас визначає кроки алгоритму й містить шаблонний метод, що складається з викликів цих кроків. Кроки можуть бути як абстрактними, так і містити реалізацію за замовчуванням.
-
Конкретний клас перевизначає деякі або всі кроки алгоритму. Конкретні класи не перевизначають сам шаблонний метод.
Псевдокод
У цьому прикладі Шаблонний метод використовується як заготовка для стандартного штучного інтелекту в простій стратегічній грі. Для введення в гру нової раси достатньо створити підклас і реалізувати в ньому відсутні методи.
Всі раси гри матимуть приблизно однакові типи юнітів та будівель, тому структура штучного інтелекту буде однаковою. Але різні раси можуть різним шляхом реалізувати ці кроки. Так, наприклад, орки будуть агресивнішими в атаці, люди більш активними в захисті, а дикі монстри взагалі не будуть займатися будівництвом.
Застосування
Якщо підкласи повинні розширювати базовий алгоритм, не змінюючи його структури.
Шаблонний метод дозволяє підкласами розширювати певні кроки алгоритму через спадкування, не змінюючи при цьому структуру алгоритмів, оголошену в базовому класі.
Якщо у вас є кілька класів, які роблять одне й те саме з незначними відмінностями. Якщо ви редагуєте один клас, тоді доводиться вносити такі ж виправлення до інших класів.
Патерн шаблонний метод пропонує створити для схожих класів спільний суперклас та оформити в ньому головний алгоритм у вигляді кроків. Кроки, які відрізняються, можна перевизначити у підкласах.
Це дозволить прибрати дублювання коду в кількох класах, які відрізняються деталями, але мають схожу поведінку.
Кроки реалізації
-
Вивчіть алгоритм і подумайте, чи можна його розбити на кроки. Вирішіть, які кроки будуть стандартними для всіх варіацій алгоритму, а які можуть бути змінюваними.
-
Створіть абстрактний базовий клас. Визначте в ньому шаблонний метод. Цей метод повинен складатися з викликів кроків алгоритму. Є сенс у тому, щоб зробити шаблонний метод фінальним, аби підкласи не могли перевизначити його (якщо ваша мова програмування це дозволяє).
-
Додайте до абстрактного класу методи для кожного з кроків алгоритму. Ви можете зробити ці методи абстрактними або додати якусь типову реалізацію. У першому випадку всі підкласи повинні будуть реалізувати ці методи, а в другому — тільки якщо реалізація кроку в підкласі відрізняється від стандартної версії.
-
Подумайте про введення хуків в алгоритм. Найчастіше хуки розташовують між основними кроками алгоритму, а також до та після всіх кроків.
-
Створіть конкретні класи, успадкувавши їх від абстрактного класу. Реалізуйте в них всі кроки та хуки, яких не вистачає.
Переваги та недоліки
- Полегшує повторне використання коду.
- Ви жорстко обмежені скелетом існуючого алгоритму.
- Ви можете порушити принцип підстановки Барбари Лісков, змінюючи базову поведінку одного з кроків алгоритму через підклас.
- У міру зростання кількості кроків шаблонний метод стає занадто складно підтримувати.
Відносини з іншими патернами
-
Фабричний метод можна розглядати як окремий випадок Шаблонного методу. Крім того, Фабричний метод нерідко буває частиною великого класу з Шаблонними методами.
-
Шаблонний метод використовує спадкування, щоб розширювати частини алгоритму. Стратегія використовує делегування, щоб змінювати «на льоту» алгоритми, що виконуються. Шаблонний метод працює на рівні класів. Стратегія дозволяє змінювати логіку окремих об’єктів.