Суть патерну

Суть патерну

Стратегія - це поведінковий патерн проектування, який визначає сімейство подібних алгоритмів і поміщає кожен із них у свій клас, після чого алгоритми можна взаємозамінювати прямо під час виконання програми.

Ви вирішили написати програму-навігатор для мандрівників. Воно має показувати гарну та зручну карту, що дозволяє з легкістю орієнтуватися у незнайомому місті.

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

Перша версія вашого навігатора могла прокладати маршрут лише дорогами, тому відмінно підходила для подорожей автомобілем. Але, вочевидь, не всі їздять у відпустку машиною. Тому наступним кроком ви додали до навігатора прокладання піших маршрутів.

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

Але це ще не все. У найближчій перспективі ви хотіли б додати прокладання маршрутів велодоріжками. А у віддаленому майбутньому — цікаві маршрути відвідин пам'яток.

Код навігатора стає занадто роздутим.

Якщо з популярністю навігатора не було жодних проблем, то технічна частина викликала питання та періодичний головний біль. З кожним новим алгоритмом код основного класу навігатора збільшувався вдвічі. У такому великому класі досить важко орієнтуватися.

Будь-яка зміна алгоритмів пошуку, чи то виправлення багів чи додавання нового алгоритму, торкнулася основного класу. Це підвищувало ризик зробитипомилку, випадково зачепивши решту працюючого коду.

Крім того, ускладнювалася командна робота з іншими програмістами, яких ви найняли після успішного релізу навігатора. Ваші зміни нерідко торкалися одного і того ж коду, створюючи конфлікти, які вимагали додаткового часу на їх вирішення.

Паттерн Стратегія пропонує визначити сімейство подібних алгоритмів, які часто змінюються чи розширюються, і винести в власні класи, звані стратегіями.

Замість того, щоб початковий клас сам виконував той чи інший алгоритм, він відіграватиме роль контексту, посилаючись на одну із стратегій та делегуючи їй виконання роботи. Щоб змінити алгоритм, вам достатньо підставити в контекст інший об'єкт-стратегію.

Важливо, щоб усі стратегії мали спільний інтерфейс. Використовуючи цей інтерфейс контекст буде незалежним від конкретних класів стратегій. З іншого боку, ви зможете змінювати та додавати нові види алгоритмів, не чіпаючи код контексту.

Стратегії побудови шляху.

У прикладі кожен алгоритм пошуку шляху переїде у свій власний клас. У цих класах буде визначено лише один метод, який приймає в параметрах координати початку і кінця шляху, а масив точок маршруту, що повертає.

Хоча кожен клас прокладатиме маршрут по-своєму, для навігатора це не матиме жодного значення, оскільки його робота полягає лише у відмальовуванні маршруту. Навігатору достатньо подати в стратегію дані про початок та кінець маршруту, щоб отримати масив точок маршруту в обумовленому форматі.

Клас навігатора буде мати метод для встановлення стратегії, дозволяючи змінювати стратегію пошуку шляху на льоту. Такий метод стане в нагоді клієнтському коду навігатора, наприклад, перемикачам типів маршрутів уінтерфейс користувача.

Аналогія з життя

Різні стратегії потрапляння до аеропорту.

Вам потрібно дістатися аеропорту. Можна доїхати автобусом, таксі або велосипедом. Тут вид транспорту є стратегією. Ви вибираєте конкретну стратегію в залежності від контексту – наявності грошей або часу до відльоту.

Контекст зберігає посилання об'єкт конкретної стратегії, працюючи із ним через загальний інтерфейс стратегій.

Стратегія визначає інтерфейс, загальний всім варіацій алгоритму. Контекст використовує цей інтерфейс для виклику алгоритму.

Для контексту неважливо, яка саме варіація алгоритму буде обрана, оскільки вони мають однаковий інтерфейс.

Конкретні стратегії реалізують різні варіації алгоритму.

Під час виконання програми контекст отримує виклики від клієнта та делегує їх об'єкту конкретної стратегії.

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

У цьому прикладі контекст використовуєСтратегію для виконання тієї чи іншої арифметичної операції.

Застосовність

Коли вам потрібно використовувати різні варіанти якогось алгоритму всередині одного об'єкта.

Стратегія дозволяє варіювати поведінку об'єкта під час виконання програми, підставляючи в нього різні об'єкти-поведінка (наприклад, що відрізняються балансом швидкості та споживання ресурсів).

Коли у вас є безліч схожих класів, які відрізняються лише деякою поведінкою.

Стратегія дозволяє винести поведінку, що відрізняється, в окрему ієрархію класів, апотім звести початкові класи одного, зробивши поведінка цього класу настроюваним.

Коли ви хочете оголювати деталі реалізації алгоритмів інших класів.

Стратегія дозволяє ізолювати код, дані та залежності алгоритмів від інших об'єктів, приховавши ці деталі всередині класів-стратегій.

Коли різні варіації алгоритмів реалізовані у вигляді розлогого умовного оператора. Кожна гілка такого оператора є варіацією алгоритму.

Стратегія вміщує кожну лапу такого оператора в окремий клас-стратегію. Потім контекст отримує певний об'єкт-стратегію від клієнта та делегує йому роботу. Якщо раптом потрібно змінити алгоритм, у контекст можна подати іншу стратегію.

Кроки реалізації

Визначте алгоритм, який піддається частим змінам. Також підійде алгоритм, що має кілька варіацій, які вибираються під час виконання програми.

Створіть інтерфейс стратегій, що описує цей алгоритм. Він має бути загальним всім варіантів алгоритму.

Розмістіть варіації алгоритму у власні класи, які реалізують цей інтерфейс.

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

Клієнти контексту повинні подавати до нього відповідний об'єкт-стратегію, коли хочуть, щоб контекст поводився певним чином.

Переваги і недоліки

  • Гаряча заміна алгоритмів на льоту.
  • Ізолює код та дані алгоритмів від інших класів.
  • Уникнення успадкування до делегування.
  • Реалізує принцип відкритості/закритості.
  • Ускладнює програму за рахунок додатковихкласів.
  • Клієнт повинен знати, в чому полягає різниця між стратегіями, щоб вибрати відповідну.

Відносини з іншими патернами

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

Команда та Стратегія схожі за духом, але відрізняються масштабом та застосуванням:

  • Команду використовують, щоб перетворити будь-які різнорідні дії на об'єкти. Параметри операції перетворюються на поля об'єкта. Цей об'єкт тепер можна логувати, зберігати в історії для скасування, передавати у зовнішні сервіси тощо.
  • З іншого боку, Стратегія визначає різні способи зробити одну й ту саму дію, дозволяючи взаємозамінювати ці способи у якомусь об'єкті контексту.

Стратегія змінює поведінку об'єкта зсередини, а Декоратор змінює його зовні.

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

Стан можна розглядати як надбудову над Стратегією. Обидва патерни використовують композицію, щоб змінювати поведінку основного об'єкта, делегуючи роботу вкладеним об'єктам-помічникам. Однак у Стратегії ці об'єкти не знають один про одного і не пов'язані. У стані самі конкретні стани можуть перемикати контекст.

Приклади реалізації патерну

Не втикай у транспорті

Краще почитай нашу книгу про патерни проектування.

Тепер це зручно робити навіть під час поїздок у громадському транспорті.