Билеты ООП / 15. Паттерн Strategy (стратегия)
.docxСтратегия (англ. Strategy) — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости. Это позволяет выбирать алгоритм путем определения соответствующего класса. Шаблон Strategy позволяет менять выбранный алгоритм независимо от объектов-клиентов, которые его используют.
Задача
По типу клиента (или по типу обрабатываемых данных) выбрать подходящий алгоритм, который следует применить. Если используется правило, которое не подвержено изменениям, нет необходимости обращаться к шаблону «стратегия».
Мотивы
-
Программа должна обеспечивать различные варианты алгоритма или поведения
-
Нужно изменять поведение каждого экземпляра класса
-
Необходимо изменять поведение объектов на стадии выполнения
-
Введение интерфейса позволяет классам-клиентам ничего не знать о классах, реализующих этот интерфейс и инкапсулирующих в себе конкретные алгоритмы
Способ решения
Отделение процедуры выбора алгоритма от его реализации. Это позволяет сделать выбор на основании контекста.
Участники
-
Класс Strategy определяет, как будут использоваться различные алгоритмы.
-
Конкретные классы ConcreteStrategy реализуют эти различные алгоритмы.
-
Класс Context использует конкретные классы ConcreteStrategy посредством ссылки на конкретный тип абстрактного класса Strategy. Классы Strategy и Context взаимодействуют с целью реализации выбранного алгоритма (в некоторых случаях классу Strategy требуется посылать запросы классу Context). Класс Context пересылает классу Strategy запрос, поступивший от его класса-клиента.
Следствия
-
Шаблон Strategy определяет семейство алгоритмов.
-
Это позволяет отказаться от использования переключателей и/или условных операторов.
-
Вызов всех алгоритмов должен осуществляться стандартным образом (все они должны иметь одинаковый интерфейс).
Достоинства паттерна Strategy
-
Систему проще поддерживать и модифицировать, так как семейство алгоритмов перенесено в отдельную иерархию классов.
-
Паттерн Strategy предоставляет возможность замены одного алгоритма другим в процессе выполнения программы.
-
Паттерн Strategy позволяет скрыть детали реализации алгоритмов от клиента.
Недостатки паттерна Strategy
-
Для правильной настройки системы пользователь должен знать об особенностях всех алгоритмов.
-
Число классов в системе, построенной с применением паттерна Strategy, возрастает.
Структура паттерна Strategy
\
UML-диаграмма классов паттерна Strategy
Реализация паттерна Strategy
using System;
namespace DesignPatterns.Behavioral.Strategy
{
// Класс реализующий конкретную стратегию, должен наследовать этот интерфейс
// Класс контекста использует этот интерфейс для вызова конкретной стратегии
public interface IStrategy
{
void Algorithm();
}
// Первая конкретная реализация-стратегия.
public class ConcreteStrategy1 : IStrategy
{
public void Algorithm()
{
Console.WriteLine("Выполняется алгоритм стратегии 1.");
}
}
// Вторая конкретная реализация-стратегия.
// Реализаций может быть сколько угодно много.
public class ConcreteStrategy2 : IStrategy
{
public void Algorithm()
{
Console.WriteLine("Выполняется алгоритм стратегии 2.");
}
}
// Контекст, использующий стратегию для решения своей задачи.
public class Context
{
// Ссылка на интерфейс IStrategy
// позволяет автоматически переключаться между конкретными реализациями
// (другими словами, это выбор конкретной стратегии).
private IStrategy _strategy;
// Конструктор контекста.
// Инициализирует объект стратегией.
public Context(IStrategy strategy)
{
_strategy = strategy;
}
// Метод для установки стратегии.
// Служит для смены стратегии во время выполнения.
// В C# может быть реализован также как свойство записи.
public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}
// Некоторая функциональность контекста, которая выбирает
//стратегию и использует её для решения своей задачи.
public void ExecuteOperation()
{
_strategy.Algorithm();
}
}
// Класс приложения.
// В данном примере выступает как клиент контекста.
public static class Program
{
// <summary>
// Точка входа в программу.
// </summary>
public static void Main()
{
// Создаём контекст и инициализируем его первой стратегией.
Context context = new Context(new ConcreteStrategy1());
// Выполняем операцию контекста, которая использует первую стратегию.
context.ExecuteOperation();
// Заменяем в контексте первую стратегию второй.
context.SetStrategy(new ConcreteStrategy2());
// Выполняем операцию контекста, которая теперь использует вторую стратегию.
context.ExecuteOperation();
}
}
}
Дополнительная информация
Назначение паттерна Strategy
Существуют системы, поведение которых может определяться согласно одному алгоритму из некоторого семейства. Все алгоритмы этого семейства являются родственными: предназначены для решения общих задач, имеют одинаковый интерфейс для использования и отличаются только реализацией (поведением). Пользователь, предварительно настроив программу на нужный алгоритм (выбрав стратегию), получает ожидаемый результат. Как пример, - приложение, предназначенное для компрессии файлов использует один из доступных алгоритмов: zip, arj или rar.
Объектно-ориентированный дизайн такой программы может быть построен на идее использования полиморфизма. В результате получаем набор родственных классов с общим интерфейсом и различными реализациями алгоритмов.
Представленному подходу свойственны следующие недостатки:
-
Реализация алгоритма жестко привязана к его подклассу, что затрудняет поддержку и расширение такой системы.
-
Система, построенная на основе наследования, является статичной. Заменить один алгоритм на другой в ходе выполнения программы уже невозможно.
Применение паттерна Strategy позволяет устранить указанные недостатки.
Описание паттерна Strategy
Паттерн Strategy переносит в отдельную иерархию классов все детали, связанные с реализацией алгоритмов. Для случая программы сжатия файлов абстрактный базовый класс Compression этой иерархии объявляет интерфейс, общий для всех алгоритмов и используемый классом Compressor. Подклассы ZIP_Compression, ARJ_Compression и RAR_Compression его реализуют в соответствии с тем или иным алгоритмом. Класс Compressor содержит указатель на объект абстрактного типа Compression и предназначен для переадресации пользовательских запросов конкретному алгоритму. Для замены одного алгоритма другим достаточно перенастроить этот указатель на объект нужного типа.