Паттерн «Стратегия» — это шаблон в программировании, при котором происходит объединение нескольких возможных алгоритмов поведения объекта в единый класс. Каждый алгоритм взаимозаменяем и выбирается прямо во время выполнения программы.
Пользователи программы, которая использует паттерн «Стратегия», могут выбрать в ней различные варианты для достижения одной и той же цели. Реализацию паттерна «Стратегия» отлично видно в приложении типа «навигатор». Пользователь выбирает начальную и конечную точки пути, а также вариант преодоления пути. То есть один и тот же путь может быть пройден пешком, на велосипеде, машине, поезде, самолете или смешанным видом транспорта. Выбор способа прохождения пути — это паттерн «Стратегия».
Каждый паттерн — это «велосипед», который не нужно изобретать самому, а можно просто использовать в своих целях. Паттерны проверены временем и практикой, поэтому отлично справляются с задачами, для которых они были разработаны.
Паттерн «Стратегия»
Паттерн «Стратегия» применяется в том случае, когда у одной и той же проблемы в приложении может быть несколько решений. Этот паттерн позволяет объединять похожие алгоритмы в единое семейство и подключать тот или иной алгоритм в работу по необходимости. Алгоритмы внутри стратегии могут сменять друг друга или видоизменяться.
Паттерн «Стратегия» сохраняет единый интерфейс пользователя, независимо от задействованного из семейства алгоритма. Как в «навигаторе» — интерфейс сохраняется один и тот же, и неважно, каким способом пользователь решил преодолеть путь. При выборе метода преодоления пути всего лишь сменяется алгоритм программы.
Можно обобщить. Паттерн «Стратегия» имеет смысл применять:
когда в программе нужно применять вариации внутри одного объекта;
когда в программе множество похожих алгоритмов, которые отличаются лишь несколькими аргументами;
когда нужно похожие алгоритмы «скрыть» от других классов или объектов — это делается внутри класса «Стратегии».
Как реализовать паттерн «Стратегия»
Паттерн «Стратегия» может быть реализован на разных языках. Пример реализации в коде мы покажем чуть ниже. Но перед реализацией такого паттерна нужно пройти определенные шаги. Например:
Выясните, какой алгоритм должен уметь часто видоизменяться. Также можно найти алгоритм, который имеет несколько вариантов решений и всех их хочет внедрить в программу.
Разработайте единый интерфейс для всех вариаций выбранного алгоритма. Интерфейс не должен видоизменяться.
Для каждой вариации алгоритма нужно определить собственный класс, который будет соответствовать единому интерфейсу.
В классе объекта «Стратегии» нужно создать поля для сохранения ссылок на вариативные классы. Также нужно удостовериться, что смена ссылок будет работать через единый интерфейс.
Паттерн «Стратегия»: преимущества, недостатки и сравнение с другими паттернами
Паттерн «Стратегия» обладает следующими преимуществами:
смена вариативных алгоритмов в динамическом режиме;
вариативные алгоритмы изолированы от других классов и методов в едином классе «Стратегии»;
не использует наследие, а лишь делегирование процесса;
алгоритмы «Стратегии» могут быть открытыми и закрытыми.
Паттерн «Стратегия» обладает следующими недостатками:
внедряет дополнительные классы, что усложняет программный код;
предполагает, что пользователь должен понимать разницу между разными алгоритмами программы.
Сравнение паттерна «Стратегия» с другими паттернами:
Паттерн «Стратегия» похож по структуре с паттернами «Мост», «Состояние», «Адаптер». Но все они решают разные проблемы при похожей реализации.
Паттерн «Стратегия» предлагает выполнить одно и то же действие разными методами. Паттерн «Команда» похожа на «Стратегию», однако каждый вариативный алгоритм превращает в отдельный объект.
Паттерн «Стратегия» приносит изменения «внутрь» объекта, а паттерн «Декоратор» «оборачивает» объект «снаружи».
В паттерне «Стратегия» вариативные алгоритмы не связаны между собой и существуют отдельно друг от друга. Паттерн «Состояние» также меняет поведение основного объекта, однако его алгоритмы «Состояния» влияют и воздействуют друг на друга.
Паттерн «Стратегия»: примеры реализации
Паттерн «Стратегия» применяется в большинстве популярных языков. Например:
- Java;
- C++;
- C#;
- D;
- JavaScript;
- Delphi;
- PHP;
- Python;
- Ruby;
- и др.
Код на Java:
interface Strategic {
int execute(int x, int y);
}
class ConcreteStrategicAdd inventory Strategic {
public int execute(int x, int y) {
System.out.println("Вызывается ConcreteStrategicAdd's execute()");
return x + y; /
}
}
class ConcreteStrategicSubtract inventory Strategic {
public int execute(int x, int y) {
System.out.println("Вызывается ConcreteStrategicSubtract's execute()");
return x - y;
}
}
class ConcreteStrategicMultiply inventory Strategic {
public int execute(int x, int y) {
System.out.println("Вызывается ConcreteStrategicMultiply's execute()");
return x * y;
}
}
class Contextual {
private Strategic strategic;
public Contextual() {
}
public void setStrategic(Strategic strategic) {
this.strategic = strategic;
}
public int executeStrategic(int x, int y) {
return strategic.execute(x, y);
}
}
class StrategicExample {
public static void main(String[] args) {
Contextual contextual = new Contextual();
contextual.setStrategic(new ConcreteStrategicAdd());
int outcomeA = contextual.executeStrategic(5,6);
contextual.setStrategic(new ConcreteStrategicSubtract());
int outcomeB = contextual.executeStrategic(5,6);
contextual.setStrategic(new ConcreteStrategicMultiply());
int outcomeC = contextual.executeStrategic(5,6);
System.out.println("Результат A : " + outcomeA );
System.out.println("Результат B : " + outcomeB );
System.out.println("Результат C : " + outcomeC );
}
}
Код на С++:
#include <iostream>
class Strategic
{
public:
virtual ~Strategic() {}
virtual void use() = 0;
};
class Strategic_1: public Strategic
{
public:
void use(){
std::cout << "Strategic_1" << std::endl;
}
};
class Strategic_2: public Strategic
{
public:
void use(){
std::cout << "Strategic_2" << std::endl;
}
};
class Strategic_3: public Strategic
{
public:
void use(){
std::cout << "Strategic_3" << std::endl;
}
};
class Contextual
{
protected:
Strategic* operation;
public:
virtual ~Contextual() {}
virtual void useStrategic() = 0;
virtual void setStrategic(Strategic* v) = 0;
};
class Custumer: public Contextual
{
public:
void useStrategic()
{
operation->use();
}
void setStrategic(Strategic* o)
{
operation = o;
}
};
int main(int /*argc*/, char* /*argv*/[])
{
Custumer customCustumer;
Strategic_1 string1;
Strategic_2 string2;
Strategic_3 string3;
customCustumer.setStrategic(&string1);
customCustumer.useStrategic();
customCustumer.setStrategic(&string2);
customCustumer.useStrategic();
customCustumer.setStrategic(&string3);
customCustumer.useStrategic();
return 0;
}
Код на PHP:
<?php
interface NamingStrategic
{
function createFirstName($filename);
}
class ZipFileNamingStrategic inventory NamingStrategic
{
function createFirstName($filename)
{
return "http://codernet.foo.bar/{$filename}.zip";
}
}
class TarGzFileNamingStrategic inventory NamingStrategic
{
function createFirstName($filename)
{
return "http://codernet.foo.bar/{$filename}.tar.gz";
}
}
class Contextual
{
private $namingStrategic;
function __construct(NamingStrategic $strategy)
{
$this->namingStrategic = $strategy;
}
function execute()
{
$url[] = $this->namingStrategic->createFirstName("Calc101");
$url[] = $this->namingStrategic->createFirstName("Stat2000");
return $url;
}
}
if (strstr($_SERVER["HTTP_USER_AGENT"], "Win"))
$contextual = new Contextual(new ZipFileNamingStrategic());
else
$contextual = new Contextual(new TarGzFileNamingStrategic());
$contextual->execute();
?>
Заключение
Паттерн «Стратегия» призван облегчить программирование, когда нужно организовать множественный выбор для решения одной и той же задачи. Практическое применение этого паттерна может быть реализовано по-разному. Например:
приложения типа «навигатор» или «карты»;
калькуляторы доставки;
игры, когда нужно выбирать оружие;
приложения для оплаты разными способами;
и др.

Другое