Стратегія (англ.Strategy) — шаблон проєктування, належить до класу шаблонів поведінки. Відомий ще під іншою назвою — «Policy». Його суть полягає у тому, щоб створити декілька схем поведінки для одного об'єкту та винести в окремий клас.
Шаблон Стратегія (Strategy) дозволяє міняти вибраний алгоритм незалежно від об'єктів-клієнтів, які його використовують.
За типом клієнта (або за типом оброблюваних даних) вибрати відповідний алгоритм, який слід застосувати. Якщо використовується правило, яке не піддається змінам, немає необхідності звертатися до шаблону «стратегія».
Структура
Мотиви
Програма повинна забезпечувати різні варіанти алгоритму або поведінки
Потрібно змінювати поведінку кожного екземпляра класу
Необхідно змінювати поведінку об'єктів на стадії виконання
Введення інтерфейсу дозволяє класам-клієнтам нічого не знати про класи, що реалізують цей інтерфейс і інкапсулюють в собі конкретні алгоритми
Спосіб вирішення
Відділення процедури вибору алгоритму від його реалізації. Це дозволяє зробити вибір на підставі контексту.
Учасники
КласStrategy визначає, як будуть використовуватися різні алгоритми.
Конкретні класи ConcreteStrategy реалізують ці різні алгоритми.
Клас Context використовує конкретні класи ConcreteStrategy за допомогою посилання на конкретний тип абстрактного класу Strategy. Класи Strategy і Context взаємодіють з метою реалізації обраного алгоритму (в деяких випадках класу Strategy потрібно надсилати запити класу Context). Клас Context пересилає класу Strategy запит, що надійшов від його класу-клієнта.
Наслідки
Шаблон Strategy визначає сімейство алгоритмів.
Це дозволяє відмовитися від використання перемикачів і / або умовних операторів.
Виклик всіх алгоритмів повинен здійснюватися стандартним чином (всі вони повинні мати однаковий інтерфейс).
Реалізація
Клас, який використовує алгоритм (Context), включає абстрактний клас (Strategy), що володіє абстрактним методом, визначальним спосіб виклику алгоритму. Кожен похідний клас реалізує один необхідний варіант алгоритму.
Використання
Архітектура Microsoft WDF заснована на цьому патерні. У кожного об'єкта «драйвер» і «пристрій» є незмінна частина, вшита в систему, в якій реєструється змінна частина (стратегія), написана в конкретній реалізації. Змінна частина може бути і зовсім порожньою, що означає, що драйвер нічого не робить, але при цьому здатний брати участь у PnP і управлінні живленням.
Бібліотека ATL містить у собі набір класів threading model, які є стратегіями (різними реалізаціями Lock / Unlock, які потім використовуються основними класами системи). При цьому в цих стратегіях використовується статичний поліморфізм через параметр шаблону, а не динамічний поліморфізм через віртуальні методи.
Призначення шаблону проєктування Стратегія
Існують системи, поведінка яких визначається відповідно до певного роду алгоритмів. Всі вони подібні між собою: призначені для вирішення спільних задач, мають однаковий інтерфейс для користування, але відрізняються тільки «поведінкою», тобто реалізацією. Користувач, налаштувавши програму на потрібний алгоритм — отримує потрібний результат.
Приклад.Є програма(інтерфейс) через яку обраховується ціна на товар для покупців у яких є знижка та ціна за сезонною знижкою — обираємо необхідний алгоритм. Об'єктно-орієнтований дизайн такої програми будується на ідеї використання поліморфізму. Результатом є набір «класів-родичів» — у яких єдиний інтерфейс та різна реалізація алгоритмів.
Недоліками такого алгоритму є те, що реалізація жорстко прив'язана до підкласу, що ускладнює внесення змін.
Вирішенням даної проблеми є використання патерну Стратегія (Strategy).
Переваги та недоліки
Переваги
Можливість позбутися умовних операторів.
Клієнт може вибирати найбільш влучну стратегію залежно від вимог щодо швидкодії і пам'яті.
Недоліки
Збільшення кількості об'єктів.
Клієнт має знати особливості реалізацій стратегій для вибору найбільш вдалої.
Зв'язок з іншими патернами
Стратегія змінює реалізацію, декоратор — доповнює
В стратегії користувач знає про класи стратегій і міняє їх самостійно, в стані різноманітні стани приховані від користувача, а за їх заміну відповідає сам клас
Міст — це структурний патерн. Його компоненти зазвичай встановлюються раз і не змінюються під час виконання програми. Використовують для розділення абстракції та реалізації. Стратегія — це шаблон поведінки. Використовують коли коли алгоритми можуть замінювати один одного під час виконання програми.
Шаблонний метод задає кроки алгоритму, які реалізовують підкласи. Стратегія задає алгоритм який можна виконати декількома способами, до того ж вибрати ці способи на етапі виконання програми
// Клас реалізує конкретну стратегію, повинен успадковувати цей інтерфейс// Клас контексту використовує цей інтерфейс для виклику конкретної стратегіїinterfaceStrategy{intexecute(inta,intb);}// Реалізуємо алгоритм з використанням інтерфейсу стратегіїclassConcreteStrategyAddimplementsStrategy{publicintexecute(inta,intb){System.out.println("Called ConcreteStrategyAdd's execute()");returna+b;// Do an addition with a and b}}classConcreteStrategySubtractimplementsStrategy{publicintexecute(inta,intb){System.out.println("Called ConcreteStrategySubtract's execute()");returna-b;// Do a subtraction with a and b}}classConcreteStrategyMultiplyimplementsStrategy{publicintexecute(inta,intb){System.out.println("Called ConcreteStrategyMultiply's execute()");returna*b;// Do a multiplication with a and b}}// Клас контексту використовує інтерфейс стратегіїclassContext{privateStrategystrategy;// ConstructorpublicContext(Strategystrategy){this.strategy=strategy;}publicintexecuteStrategy(inta,intb){returnstrategy.execute(a,b);}}// Тестовий додатокclassStrategyExample{publicstaticvoidmain(String[]args){Contextcontext;context=newContext(newConcreteStrategyAdd());intresultA=context.executeStrategy(3,4);context=newContext(newConcreteStrategySubtract());intresultB=context.executeStrategy(3,4);context=newContext(newConcreteStrategyMultiply());intresultC=context.executeStrategy(3,4);}}
#include<iostream>usingnamespacestd;// загальний спосіб вирішення проблемиstructNameStrategy{virtualvoidgreet()=0;};// Конкретні рецепти вирішенняstructSayHi:publicNameStrategy{voidgreet(){cout<<" Hi!How is it going ? "<<endl;}};structIgnore:publicNameStrategy{voidgreet(){cout<<" (Pretend I do not see you)"<<endl;}};structAdmission:publicNameStrategy{voidgreet(){cout<<" I am sorry.I forgot your name."<<endl;}};// Контекст керує стратегією («Посередник»)// «Стан» - якщо можлива зміна стратегії за життя контекстуclassContext{private:NameStrategy&strategy;public:Context(NameStrategy&strat):strategy(strat){}voidgreet(){strategy.greet();}// постійний код};voidmain(){SayHisayhi;Ignoreignore;Admissionadmission;Contextc1(sayhi),c2(ignore),c3(admission);c1.greet();c2.greet();c3.greet();}
usingSystem;namespaceDesignPatterns.Behavioral.Strategy{/// <summary>/// Інтерфейс «Стратегія» визначає функціональність (в даному прикладі це метод/// <see Cref="Algorithm"> Algorithm </see>), яка повинна бути реалізована/// конкретними класами стратегій. Іншими словами, метод інтерфейсу визначає/// вирішення якоїсь задачі, а його реалізації в конкретних класах стратегій визначають,/// яким шляхом ця задача буде вирішена./// </ Summary>publicinterfaceIStrategy{voidAlgorithm();}/// <summary>/// Перша конкретна реалізація-стратегія./// </summary>publicclassConcreteStrategy1:IStrategy{publicvoidAlgorithm(){Console.WriteLine("Виконується алгоритм стратегії 1.");}}/// <summary>/// Друга конкретна реалізація-стратегія./// Реалізацій може бути скільки завгодно багато./// </Summary>publicclassConcreteStrategy2:IStrategy{publicvoidAlgorithm(){Console.WriteLine("Виконується алгоритм стратегії 2.");}}/// <summary>/// Контекст, використовує стратегію для вирішення свого завдання./// </summary>publicclassContext{/// <summary>/// Посилання на інтерфейс <see cref="IStrategy">IStrategy</see>/// дозволяє автоматично перемикатися між конкретними реалізаціями/// (іншими словами, це вибір конкретної стратегії)./// </summary>privateIStrategy_strategy;/// <summary>/// Конструктор контексту./// Ініціалізує об'єкт стратегією./// </summary>/// <param name="strategy">/// Стратегія./// </param>publicContext(IStrategystrategy){_strategy=strategy;}/// <summary>/// Метод для установки стратегії./// Служить для зміни стратегії під час виконання./// В C# може бути реалізований так само як властивість запису./// </summary>/// <param name="strategy">/// Нова стратегія./// </param>publicvoidSetStrategy(IStrategystrategy){_strategy=strategy;}/// <summary>/// Деяка функціональність контексту, яка вибирає/// стратегію і використовує її для вирішення свого завдання./// </summary>publicvoidExecuteOperation(){_strategy.Algorithm();}}/// <summary>/// Клас додатка./// У даному прикладі виступає як клієнт контексту./// </summary>publicstaticclassProgram{/// <summary>/// Точка входу в програму./// </summary>publicstaticvoidMain(){// Створюємо контекст і ініціалізували його першої стратегією.Contextcontext=newContext(newConcreteStrategy1());// Виконуємо операцію контексту, яка використовує першу стратегію.context.ExecuteOperation();// Замінюємо в контексті першу стратегію другою.context.SetStrategy(newConcreteStrategy2());// Виконуємо операцію контексту, яка тепер використовує другу стратегію.context.ExecuteOperation();}}}
// "інтерфейс" StrategyclassStrategy{exec(){}}// реалізації Strategy// показ повідомлення в статусному рядку вебоглядача// (підтримується не всіма вебоглядачами)classStrategyWindowStatusextendsStrategy{constructor(){super();}exec(message){window.status=message;}}classStrategyNewWindowextendsStrategy{constructor(){super();}exec(message){constwin=window.open("","_blank");win.document.write("<html>"+message+"</html>");}}// показ повідомлення за допомогою модального вікнаclassStrategyAlertextendsStrategy{constructor(){super();}exec(message){alert(message);}}// ContextclassContext{constructor(strategy){this.strategy=undefined;}exec(message){this.strategy.exec(message);}}// ВикористанняconstshowInWindowStatus=newContext(newStrategyWindowStatus());constshowInNewWindow=newContext(newStrategyNewWindow());constshowInAlert=newContext(newStrategyAlert());showInWindowStatus.exec("повідомлення");showInNewWindow.exec("повідомлення");showInAlert.exec("повідомлення");
Приклад з використанням динамічних (first-class) функцій
classPeople(object):tool=Nonedef__init__(self,name):self.name=namedefsetTool(self,tool):self.tool=tooldefwrite(self,text):self.tool.write(self.name,text)classToolBase:""" Сімейство алгоритмів `Інструмент написання` """defwrite(self,name,text):raiseNotImplementedErrorclassPenTool(ToolBase):"""Ручка"""defwrite(self,name,text):printu'%s (ручкою) %s'%(name,text)classBrushTool(ToolBase):"""Пензель"""defwrite(self,name,text):printu'%s (пензлем) %s'%(name,text)classStudent(People):"""Студент"""tool=PenTool()classPainter(People):"""Художник"""tool=BrushTool()alexandr=Student(u'Олександр')alexandr.write(u'Пишу лекцію про шаблон Стратегія')# Олександр (ручкою) Пишу лекцію про шаблон Стратегіяsolomia=Painter(u'Соломія')solomia.write(u'Малюю ілюстрацію до шаблону Стратегія')# Соломія (пензлем) Малюю ілюстрацію до шаблону Стратегія# Соломія вирішила стати студентомsolomia.setTool(PenTool())solomia.write(u'Ні, вже краще я напишу конспект')# Соломія (ручкою) Ні, вже краще я напишу конспект
Висновки
Останнім часом розроблено багато мов програмування, але в кожній з них для досягнення найкращого результату роботи необхідно використовувати шаблони програмування, одним з яких є Стратегія (Strategy).