Стратегія (шаблон проєктування): відмінності між версіями
[неперевірена версія] | [неперевірена версія] |
Вилучено вміст Додано вміст
м Додавання/виправлення дати до Шаблон:Стаття, з якої нема посилань |
оновлення даних, доповнення, правопис, стильові правлення |
||
Рядок 1:
'''Стратегія''' ({{lang-en|Strategy }}) —[[шаблони проектування|шаблон проектування]], відноситься до класу [[шаблони поведінки|шаблонів поведінки]]. Відомий ще під іншою назвою - "Policy". Його суть полягає у тому, щоб створити декілька схем поведінки для одного об'єкту та винести в окремий клас.
Шаблон Стратегія(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.
== Посилання ==
[http://modis.ispras.ru/Lizorkin/private/patterns.pdf modis.ispras.ru/Lizorkin/private/patterns.pdf]<br />
[[Категорія:Шаблони проектування програмного забезпечення]]
|