Стратегія (шаблон проєктування): відмінності між версіями

[неперевірена версія][неперевірена версія]
Вилучено вміст Додано вміст
DixonDBot (обговорення | внесок)
м Додавання/виправлення дати до Шаблон:Стаття, з якої нема посилань
оновлення даних, доповнення, правопис, стильові правлення
Рядок 1:
'''Стратегія''' ({{lang-en|Strategy }}) —[[шаблони проектування|шаблон проектування]], відноситься до класу [[шаблони поведінки|шаблонів поведінки]]. Відомий ще під іншою назвою - "Policy". Його суть полягає у тому, щоб створити декілька схем поведінки для одного об'єкту та винести в окремий клас.
{{Стаття, з якої нема посилань|дата=Квітень 2012}}
Шаблон Стратегія(Strategy) дозволяє міняти вибраний алгоритм незалежно від [[Об'єкт (програмування)|об'єктів-клієнтів,]] які його використовують.
<big>
== Патерн Strategy (Стратегія) ==
</big>
Цей патерн відомий ще під іншою назвою - "Policy". Його суть полягає у тому, щоб створити декілька схем поведінки для одного об'єкту та винести в окремий клас.
 
== Основні характеристики ==
=== Завдання ===
=== Призначення патерну Strategy ===
Існують системи, поведінка яких визначається відповідно до певного роду алгоритмів. Всі вони подібні між собою: призначені для вирішення спільних задач, мають однаковий інтерфейс для користування, але відрізняються тільки "поведінкою", тобто реалізацією. Користувач, налаштувавши програму на потрібний алгоритм - отримує потрібний результат. <br />
'''Приклад.''' Є програма(інтерфейс) через яку обраховується ціна на товар для покупців у яких є знижка та ціна за сезонною знижкою - обираємо необхідний алгоритм. Об'єктно-орієнтованиий дизайн такої програми будується на ідеї використання поліморфізму. Результатом є набір "класів-родичів" - у яких єдиний інтерфейс та різна реалізація алгоритмів.<br />
''Недоліками'' такого алгоритму є те, що реалізація жорстко прив'язана до підкласу, що ускладнює внесення змін. <br />
''Вирішенням'' даної проблеми є використання патерну Стратегія (Strategy).
 
За типом клієнта (або за типом оброблюваних даних) вибрати підходящий алгоритм, який слід застосувати. Якщо використовується правило, яке не піддається змінам, немає необхідності звертатися до шаблону «стратегія».
=== Опис патерну Strategy ===
Реалізувати програму, якою рахуватиметься знижка для покупця, можна за допомогою патерну Strategy. Для цього створюється декілька класів "Стратегія", кожен з яких містить один і той же поліморфний метод "Порахувати вартість". Як параметри в метод передаються дані про продаж. Об'єкт Strategy має зв'язок з конкретним об'єктом - для якого використовується алгоритм.
 
=== Структура ===
[[Image:Strategy Pattern.jpg|thumb|none|450px| ]] [[Image:Strategy pattern in LePUS3.gif|thumb|none|450px|]]
 
=== Мотиви ===
 
* Програма повинна забезпечувати різні варіанти алгоритму або поведінки
* Потрібно змінювати поведінку кожного екземпляра класу
* Необхідно змінювати поведінку об'єктів на стадії виконання
* Введення інтерфейсу дозволяє класам-клієнтам нічого не знати про класи, що реалізують цей інтерфейс і інкапсулюють в собі конкретні алгоритми
 
=== Спосіб вирішення ===
 
Відділення процедури вибору алгоритму від його реалізації. Це дозволяє зробити вибір на підставі контексту.
 
=== Учасники ===
 
* [[Клас (програмування)|Клас]] ''<code>Strategy</code>'' визначає, як будуть використовуватися різні алгоритми.
* Конкретні класи <code>ConcreteStrategy</code> реалізують ці різні алгоритми.
* Клас <code>Context</code> використовує конкретні класи <code>ConcreteStrategy</code> допомогою посилання на конкретний тип абстрактного класу ''<code>Strategy</code>'' . Класи ''<code>Strategy</code>'' і <code>Context</code> взаємодіють з метою реалізації обраного алгоритму (в деяких випадках класу ''<code>Strategy</code>'' потрібно посилати запити класу <code>Context</code> ). Клас <code>Context</code> пересилає класу ''<code>Strategy</code>'' запит, що надійшов від його класу-клієнта.
 
=== Слідства ===
 
* Шаблон Strategy визначає сімейство алгоритмів.
* Це дозволяє відмовитися від використання перемикачів і / або умовних операторів.
* Виклик всіх алгоритмів повинен здійснюватися стандартним чином (всі вони повинні мати однаковий інтерфейс).
 
=== Реалізація ===
 
Клас, який використовує алгоритм ( <code>Context</code> ), включає абстрактний клас ( ''<code>Strategy</code>'' ), що володіє абстрактним методом, визначальним спосіб виклику алгоритму. Кожен похідний клас реалізує один необхідний варіант алгоритму.
 
=== Використання ===
 
Архітектура Microsoft WDF заснована на цьому паттерне. У кожного об'єкта "драйвер" і "пристрій" є незмінна частина, вшита в систему, в якій реєструється змінна частина (стратегія), написана в конкретній реалізації. Змінна частина може бути і зовсім порожній, що дасть нічого не робить драйвер, але при цьому здатний брати участь у PnP і управління живленням.
 
Бібліотека [[ATL|ATL]] містить у собі набір класів threading model, які є стратегіями (різними реалізаціями Lock / Unlock, які потім використовуються основними класами системи). При цьому в цих стратегіях використовується статичний поліморфізм через параметр шаблону, а не динамічний поліморфізм через віртуальні методи.
 
=== Призначення шаблону проектування Стратегія ===
Існують системи, поведінка яких визначається відповідно до певного роду алгоритмів. Всі вони подібні між собою: призначені для вирішення спільних задач, мають однаковий інтерфейс для користування, але відрізняються тільки "поведінкою", тобто реалізацією. Користувач, налаштувавши програму на потрібний алгоритм - отримує потрібний результат. <br />
:'''Приклад.''' ''Є програма(інтерфейс) через яку обраховується ціна на товар для покупців у яких є знижка та ціна за сезонною знижкою - обираємо необхідний алгоритм. Об'єктно-орієнтованиий дизайн такої програми будується на ідеї використання поліморфізму. Результатом є набір "класів-родичів" - у яких єдиний інтерфейс та різна реалізація алгоритмів.<br />
:
* Недоліками такого алгоритму є те, що реалізація жорстко прив'язана до підкласу, що ускладнює внесення змін. <br />
:
* Вирішенням даної проблеми є використання патерну Стратегія (Strategy).''
:
=== Переваги ===
# Можливість позбутися умовних операторів.<br />
Рядок 22 ⟶ 57:
# Збільшення кількості об'єктів.<br />
# Клієнт має знати особливості реалізацій стратегій для вибору найбільш вдалої.<br />
 
=== Використання ===
Архітектура Microsoft WDF заснована на цьому шаблоні. У кожного об'єкта "драйвер" і "пристрій" є незмінна частина, вшита в систему, в якій реєструється змінна частина (стратегія), написана в конкретній реалізації. Змінна частина може бути і зовсім порожній, що дасть нічого не робить драйвер, але при цьому здатний брати участь у PnP і управління живленням.
 
Бібліотека [[ATL|ATL]] містить у собі набір класів threading model, які є стратегіями (різними реалізаціями Lock / Unlock, які потім використовуються основними класами системи). При цьому в цих стратегіях використовується статичний поліморфізм через параметр шаблону, а не динамічний поліморфізм через віртуальні методи.
 
=== Приклади ===
'''Приклад на [[Java|Java]]'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="java">
// Клас реалізує конкретну стратегію, повинен успадковувати цей інтерфейс
// Клас контексту використовує цей інтерфейс для виклику конкретної стратегії
interface Strategy {
int execute(int a, int b);
}
 
// Реалізуємо алгоритм з використанням інтерфейсу стратегії
class ConcreteStrategyAdd implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyAdd's execute()");
return a + b; // Do an addition with a and b
}
}
class ConcreteStrategySubtract implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategySubtract's execute()");
return a - b; // Do a subtraction with a and b
}
}
class ConcreteStrategyMultiply implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyMultiply's execute()");
return a * b; // Do a multiplication with a and b
}
}
 
// Клас контексту використовує інтерфейс стратегії
class Context {
private Strategy strategy;
// Constructor
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
// Тестовий додаток
class StrategyExample {
public static void main(String[] args) {
Context context;
context = new Context(new ConcreteStrategyAdd());
int resultA = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategySubtract());
int resultB = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategyMultiply());
int resultC = context.executeStrategy(3,4);
}
}
</source>
}}
 
'''Приклад на [[C++|C + +]]'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="cpp">
 
class Strategy
{
public:
Strategy(void){}
~Strategy(void){}
 
virtual void use(void) = 0;
};
 
class Strategy_1: public Strategy
{
public:
Strategy_1(){}
~Strategy_1(){}
 
void use(void){ cout << "Strategy_1" << endl; };
};
 
class Strategy_2: public Strategy
{
public:
Strategy_2(){}
~Strategy_2(){}
 
void use(void){ cout << "Strategy_2" << endl; };
};
 
class Strategy_3: public Strategy
{
public:
Strategy_3(){}
~Strategy_3(){}
 
void use(void){ cout << "Strategy_3" << endl; };
};
 
class Context
{
protected:
Strategy* operation;
 
public:
Context(void){}
~Context(void){}
 
virtual void UseStrategy(void) = 0;
virtual void SetStrategy(Strategy* v) = 0;
};
 
class Client: public Context
{
public:
Client(void){}
~Client(void){}
 
void UseStrategy(void)
{
operation->use();
}
 
void SetStrategy(Strategy* o)
{
operation = o;
}
};
 
int _tmain(int argc, _TCHAR* argv[])
{
Client customClient;
Strategy_1 str1;
Strategy_2 str2;
Strategy_3 str3;
 
customClient.SetStrategy(&str1);
customClient.UseStrategy();
customClient.SetStrategy(&str2);
customClient.UseStrategy();
customClient.SetStrategy(&str3);
customClient.UseStrategy();
 
return 0;
}
</source>
}}
 
'''Приклад на [[C Sharp|C #]]'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="csharp">
using System;
 
namespace DesignPatterns.Behavioral.Strategy
{
/// <summary>
/// Інтерфейс «Стратегія» визначає функціональність (в даному прикладі це метод
 /// <see Cref="Algorithm"> Algorithm </see>), яка повинна бути реалізована
 /// конкретними класами стратегій. Іншими словами, метод інтерфейсу визначає
 /// вирішення якоїсь задачі, а його реалізації в конкретних класах стратегій визначають,
 /// яким шляхом ця задача буде вирішена.
 /// </ Summary>
public interface IStrategy
{
void Algorithm();
}
 
/// <summary>
/// Перша конкретна реалізація-стратегія.
/// </summary>
public class ConcreteStrategy1 : IStrategy
{
public void Algorithm()
{
Console.WriteLine("Виконується алгоритм стратегії 1.");
}
}
 
/// <summary>
/// Друга конкретна реалізація-стратегія.
    /// Реалізацій може бути скільки завгодно багато.
    /// </Summary>
public class ConcreteStrategy2 : IStrategy
{
public void Algorithm()
{
Console.WriteLine("Виконується алгоритм стратегії 2.");
}
}
 
/// <summary>
/// Контекст, використовує стратегію для вирішення свого завдання.
/// </summary>
public class Context
{
/// <summary>
/// Посилання на інтерфейс <see cref="IStrategy">IStrategy</see>
/// дозволяє автоматично перемикатися між конкретними реалізаціями
/// (іншими словами, це вибір конкретної стратегії).
/// </summary>
private IStrategy _strategy;
 
/// <summary>
/// Конструктор контексту.
/// Ініціалізує об'єкт стратегією.
/// </summary>
/// <param name="strategy">
/// Стратегія.
/// </param>
public Context(IStrategy strategy)
{
_strategy = strategy;
}
 
/// <summary>
/// Метод для установки стратегії.
/// Служить для зміни стратегії під час виконання.
/// В C# може бути реалізований так само як властивість запису.
/// </summary>
/// <param name="strategy">
/// Нова стратегія.
/// </param>
public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}
 
/// <summary>
/// Деяка функціональність контексту, яка вибирає
/// стратегію і використовує її для вирішення свого завдання.
/// </summary>
public void ExecuteOperation()
{
_strategy.Algorithm();
}
}
 
/// <summary>
/// Клас додатка.
/// У даному прикладі виступає як клієнт контексту.
/// </summary>
public static class Program
{
/// <summary>
/// Точка входу в програму.
/// </summary>
public static void Main()
{
// Створюємо контекст і ініціалізували його першої стратегією.
Context context = new Context(new ConcreteStrategy1());
// Виконуємо операцію контексту, яка використовує першу стратегію.
context.ExecuteOperation();
// Замінюємо в контексті першу стратегію другою.
context.SetStrategy(new ConcreteStrategy2());
// Виконуємо операцію контексту, яка тепер використовує другу стратегію.
context.ExecuteOperation();
}
}
}
</source>
}}
 
'''Приклади на D'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="D">
import std.stdio;
 
interface IStrategy
{
int Action(int a, int b);
}
 
class TAddition: IStrategy
{
public int Action(int a, int b)
{
return a+b;
}
}
 
class TSubtraction: IStrategy
{
public int Action(int a, int b)
{
return a-b;
}
}
 
class TContexet
{
private:
int a, b;
IStrategy strategy;
public:
void SetAB(int a, int b)
{
TContexet.a = a;
TContexet.b = b;
};
void SetStrategy(IStrategy strategy)
{
TContexet.strategy = strategy;
}
int Action()
{
return strategy.Action(a, b);
}
}
 
void main()
{
TContexet context = new TContexet;
context.SetAB(10, 5);
context.SetStrategy(new TAddition);
writeln(context.Action()); // 15
context.SetStrategy(new TSubtraction);
writeln(context.Action()); // 5
}
 
</source>
}}
 
'''Приклад на Delphi'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="delphi">
program Strategy_pattern;
 
{$APPTYPE CONSOLE}
 
type
IStrategy = interface
['{6105F24C-E5B2-47E5-BE03-835A894DEB42}']
procedure Algorithm;
end;
 
TConcreteStrategy1 = class(TInterfacedObject, IStrategy)
public
procedure Algorithm;
end;
 
procedure TConcreteStrategy1.Algorithm;
begin
Writeln('TConcreteStrategy1.Algorithm');
end;
 
type
TConcreteStrategy2 = class(TInterfacedObject, IStrategy)
public
procedure Algorithm;
end;
 
procedure TConcreteStrategy2.Algorithm;
begin
Writeln('TConcreteStrategy2.Algorithm');
end;
 
type
TContext = class
private
FStrategy: IStrategy;
public
procedure ContextMethod;
property Strategy: IStrategy read FStrategy write FStrategy;
end;
 
procedure TContext.ContextMethod;
begin
FStrategy.Algorithm;
end;
 
var
Context: TContext;
begin
Context := TContext.Create;
try
Context.Strategy := TConcreteStrategy1.Create;
Context.ContextMethod;
Context.Strategy := TConcreteStrategy2.Create;
Context.ContextMethod;
finally
Context.Free;
end;
end.
</source>
}}
 
'''Приклади на Javascript'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="javascript">
// "інтерфейс" Strategy
 
function Strategy() {
this.exec = function() {};
};
 
 
// реалізації Strategy
 
// показ повідомлення в статусному рядку веб-оглядача
// (підтримується не всіма веб-оглядачами)
function StrategyWindowStatus() {
this.exec = function(message) {
window.status = message;
};
};
StrategyWindowStatus.prototype = new Strategy();
StrategyWindowStatus.prototype.constructor = StrategyWindowStatus;
 
function StrategyNewWindow() {
this.exec = function(message) {
var win = window.open("", "_blank");
win.document.write("<html>"+ message +"</html>");
};
};
StrategyNewWindow.prototype = new Strategy();
StrategyNewWindow.prototype.constructor = StrategyNewWindow;
 
// показ повідомлення за допомогою модального вікна
function StrategyAlert() {
this.exec = function(message) {
alert(message);
};
};
StrategyAlert.prototype = new Strategy();
StrategyAlert.prototype.constructor = StrategyAlert;
 
 
// Context
 
function Context(strategy) {
this.exec = function(message) {
strategy.exec(message);
};
}
 
 
// Використання
 
var showInWindowStatus = new Context( new StrategyWindowStatus() );
var showInNewWindow = new Context( new StrategyNewWindow() );
var showInAlert = new Context( new StrategyAlert() );
 
showInWindowStatus.exec("повідомлення");
showInNewWindow.exec("повідомлення");
showInAlert.exec("повідомлення");
</source>
}}
 
'''Приклад з використанням динамічних (first-class) функцій'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="javascript">
function Context(fn) {
this.exec = function() {
fn.apply(this, arguments || []);
};
};
 
var showInWindowStatus = new Context( function(message) {
window.status = message;
} );
var showInNewWindow = new Context( function(message) {
var win = window.open("", "_blank");
win.document.write("<html>"+ message +"</html>");
} );
var showInAlert = new Context( function(message) {
alert(message);
} );
 
showInWindowStatus.exec("повідомлення");
showInNewWindow.exec("повідомлення");
showInAlert.exec("повідомлення");
</source>
}}
 
'''Приклади на PHP5'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="php">
<?php
interface NamingStrategy
{
function createName($filename);
}
class ZipFileNamingStrategy implements NamingStrategy
{
function createName($filename)
{
return "http://downloads.foo.bar/{$filename}.zip";
}
}
class TarGzFileNamingStrategy implements NamingStrategy
{
function createName($filename)
{
return "http://downloads.foo.bar/{$filename}.tar.gz";
}
}
 
class Context
{
private $namingStrategy;
function __construct(NamingStrategy $strategy)
{
$this->namingStrategy = $strategy;
}
function execute()
{
$url[] = $this->namingStrategy->createName("Calc101");
$url[] = $this->namingStrategy->createName("Stat2000");
 
return $url;
}
}
 
if (strstr($_SERVER["HTTP_USER_AGENT"], "Win"))
$context = new Context(new ZipFileNamingStrategy());
else
$context = new Context(new TarGzFileNamingStrategy());
 
$context->execute();
?>
</source>
}}
 
'''Приклад на [[Python|Python]]'''
{{Hider|
title = '''Приклад реалізації''' |
content-style = text-align: left; |
hidden = true |
content =
<source lang="python">
class People(object):
tool = None
def __init__(self, name):
self.name = name
def setTool(self, tool):
self.tool = tool
def write(self, text):
self.tool.write(self.name, text)
class ToolBase:
"""
Сімейство алгоритмів `Інструмент написання`
"""
def write(self, name, text):
raise NotImplementedError
class PenTool(ToolBase):
"""Ручка"""
def write(self, name, text):
print u'%s (ручкой) %s' % (name, text)
class BrushTool(ToolBase):
"""Пензель"""
def write(self, name, text):
print u'%s (пензлем) %s' % (name, text)
 
class Student(People):
"""Студент"""
tool = PenTool()
class Painter(People):
"""Художник"""
tool = BrushTool()
 
alexandr = Student(u'Олександр')
alexandr.write(u'Пишу лекцію про шаблон Стратегія')
# Олександр (ручкою) Пишу лекцію про шаблон Стратегія
 
solomia = Painter(u'Соломія')
solomia.write(u'Малюю ілюстрацію до шаблону Стратегія')
# Соломія (пензлем) Малюю ілюстрацію до шаблону Стратегія
 
# Соломія вирішила стати студентом
solomia.setTool(PenTool())
solomia.write(u'Ні, вже краще я напишу конспект')
# Соломія (ручкою) Ні, вже краще я напишу конспект
</source>
}}
 
== Висновки ==
Останнім часом розроблено багато мов програмування, але в кожній з них для досягнення найкращого результату роботи необхідно використовувати шаблони програмування, одним з яких є Стратегія (Strategy).
==Джерела==
 
[http://www.uml.org.cn/c++/pdf/DesignPatterns.pdf Design Patterns: Elements of Reusable Object-Oriented Software]
== Література ==
* Bishop, Judith. C# 3.0 Design Patterns. Sebastopol, California: O’Reilly, 2008.
* Tomas Petricek, Jon Skeet. Functional Programming for the Real World. б.м.: Manning Publications, 2010.
 
Tomas Petricek, Jon Skeet. Functional Programming for the Real World. б.м.: Manning Publications, 2010.
 
== Посилання ==
[http://modis.ispras.ru/Lizorkin/private/patterns.pdf modis.ispras.ru/Lizorkin/private/patterns.pdf]<br />
[http://habrahabr.ru/post/120375/]<br />
[http://ru.wikipedia.org/wiki/Стратегия]
 
[[Категорія:Шаблони проектування програмного забезпечення]]