Паттерн Стратегия (Strategy) представляет шаблон проектирования, который определяет набор алгоритмов, инкапсулирует каждый из них и обеспечивает их взаимозаменяемость. В зависимости от ситуации мы можем легко заменить один используемый алгоритм другим. При этом замена алгоритма происходит независимо от объекта, который использует данный алгоритм.
Когда использовать стратегию?
- Когда есть несколько родственных классов, которые отличаются поведением. Можно задать один основной класс, а разные варианты поведения вынести в отдельные классы и при необходимости их применять
- Когда необходимо обеспечить выбор из нескольких вариантов алгоритмов, которые можно легко менять в зависимости от условий
- Когда необходимо менять поведение объектов на стадии выполнения программы
- Когда класс, применяющий определенную функциональность, ничего не должен знать о ее реализации
Формально паттерн Стратегия можно выразить следующей схемой UML:
C++. Паттерн проектирования программ «Стратегия (Strategy)».
Формальное определение паттерна на языке C# может выглядеть следующим образом:
public interface IStrategy < void Algorithm(); >public class ConcreteStrategy1 : IStrategy < public void Algorithm() <>> public class ConcreteStrategy2 : IStrategy < public void Algorithm() <>> public class Context < public IStrategy ContextStrategy < get; set; >public Context(IStrategy _strategy) < ContextStrategy = _strategy; >public void ExecuteAlgorithm() < ContextStrategy.Algorithm(); >>
Участники
Как видно из диаграммы, здесь есть следующие участники:
- Интерфейс IStrategy, который определяет метод Algorithm() . Это общий интерфейс для всех реализующих его алгоритмов. Вместо интерфейса здесь также можно было бы использовать абстрактный класс.
- Классы ConcreteStrategy1 и ConcreteStrategy, которые реализуют интерфейс IStrategy, предоставляя свою версию метода Algorithm() . Подобных классов-реализаций может быть множество.
- Класс Context хранит ссылку на объект IStrategy и связан с интерфейсом IStrategy отношением агрегации.
В данном случае объект IStrategy заключена в свойстве ContextStrategy, хотя также для нее можно было бы определить приватную переменную, а для динамической установки использовать специальный метод.
Теперь рассмотрим конкретный пример. Существуют различные легковые машины, которые используют разные источники энергии: электричество, бензин, газ и так далее. Есть гибридные автомобили. В целом они похожи и отличаются преимущественно видом источника энергии. Не говоря уже о том, что мы можем изменить применяемый источник энергии, модифицировав автомобиль. И в данном случае вполне можно применить паттерн стратегию:
class Program < static void Main(string[] args) < Car auto = new Car(4, «Volvo», new PetrolMove()); auto.Move(); auto.Movable = new ElectricMove(); auto.Move(); Console.ReadLine(); >> interface IMovable < void Move(); >class PetrolMove : IMovable < public void Move() < Console.WriteLine(«Перемещение на бензине»); >> class ElectricMove : IMovable < public void Move() < Console.WriteLine(«Перемещение на электричестве»); >> class Car < protected int passengers; // кол-во пассажиров protected string model; // модель автомобиля public Car(int num, string model, IMovable mov) < this.passengers = num; this.model = model; Movable = mov; >public IMovable Movable < private get; set; >public void Move() < Movable.Move(); >>
В данном случае в качестве IStrategy выступает интерфейс IMovable, определяющий метод Move() . А реализующий этот интерфейс семейство алгоритмов представлено классами ElectricMove и PetroleMove. И данные алгоритмы использует класс Car.
Важнейший шаблон программирования из 23-х
Источник: metanit.com
Паттерн «Стратегия» (Strategy)
Назначение: определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Стратегия позволяет изменять алгоритмы независимо от клиентов, которые ими пользуются.
Другими словами: стратегия инкапсулирует определенное поведение с возможностью его подмены.
Когда использовать стратегию?
- Когда есть несколько схожих классов , которые отличаются поведением. Можно задать один основной класс, а разные варианты поведения вынести в отдельные классы и при необходимости их применять;
- Когда необходимо обеспечить выбор из нескольких вариантов решений, которые можно легко менять в зависимости от условий;
- Когда необходимо менять поведение классов и объектов на стадии выполнения программы;
- Когда класс, применяющий определенную функциональность, ничего не должен знать о ее реализации
Паттерн «стратегия» можно выразить на схеме UML таким образом:
Реализация схемы на языке C#
/// /// The ‘Strategy’ abstract class /// public abstract class Strategy < public abstract void AlgorithmInterface(); >/// /// A ‘ConcreteStrategy’ class /// public class ConcreteStrategyA : Strategy < public override void AlgorithmInterface() < Console.WriteLine( «Called ConcreteStrategyA.AlgorithmInterface()»); >> /// /// A ‘ConcreteStrategy’ class /// public class ConcreteStrategyB : Strategy < public override void AlgorithmInterface() < Console.WriteLine( «Called ConcreteStrategyB.AlgorithmInterface()»); >> /// /// A ‘ConcreteStrategy’ class /// public class ConcreteStrategyC : Strategy < public override void AlgorithmInterface() < Console.WriteLine( «Called ConcreteStrategyC.AlgorithmInterface()»); >> /// /// The ‘Context’ class /// public class Context < private readonly Strategy _strategy; // Constructor public Context(Strategy strategy) < _strategy = strategy; >public void ContextInterface() < _strategy.AlgorithmInterface(); >>
Участники:
Из диаграммы и кода можно выделить таких участников:
- Абстрактный класс Strategy (он может быть заменен интерфейсом), который определяет метод AlgorithmInterface() .
- Классы ConcreteStrategyA и ConcreteStrategyB, ConcreteStrategyC, которые реализуют Strategy, предоставляя переопределяя метод AlgorithmInterface() . Подобных классов-реализаций может быть множество.
- Класс Context хранит ссылку на объект Strategy и связан с абстрактным классом Strategy отношением агрегации.
Неформальная схема паттерна выглядит вот так:
Примеры реализации в .net framework
Паттерн стратегия очень распространенный паттерн в фреймворке:
- LINQ (Language Integrated Query) — это набор методов расширения, принимающих стратегии фильтрации, получения проекции и т. д. Коллекции принимают стратегии сравнения элементов, а значит, любой класс, который принимает IComparer или IEqualityComparer, использует паттерн «Стратегия».
- WCF просто переполнен стратегиями: IErrorHandler — стратегия обработки коммуникационных ошибок; IChannelInitializer — стратегия инициализации канала; IDispatchMessageFormatter — стратегия форматирования сообщений; MessageFilter — стратегия фильтрации сообщений и т. д. Обилие стратегий наблюдается также в Windows Forms, WPF, ASP.NET и других фреймворках.
Источник: bool.dev
Паттерн Strategy (стратегия)
Существуют системы, поведение которых может определяться согласно одному алгоритму из некоторого семейства. Все алгоритмы этого семейства являются родственными: предназначены для решения общих задач, имеют одинаковый интерфейс для использования и отличаются только реализацией (поведением). Пользователь, предварительно настроив программу на нужный алгоритм (выбрав стратегию), получает ожидаемый результат. Как пример, — приложение, предназначенное для компрессии файлов использует один из доступных алгоритмов: zip, arj или rar.
Объектно-ориентированный дизайн такой программы может быть построен на идее использования полиморфизма. В результате получаем набор родственных классов с общим интерфейсом и различными реализациями алгоритмов.
Представленному подходу свойственны следующие недостатки:
- Реализация алгоритма жестко привязана к его подклассу, что затрудняет поддержку и расширение такой системы.
- Система, построенная на основе наследования, является статичной. Заменить один алгоритм на другой в ходе выполнения программы уже невозможно.
Применение паттерна Strategy позволяет устранить указанные недостатки.
Описание паттерна Strategy
Паттерн Strategy переносит в отдельную иерархию классов все детали, связанные с реализацией алгоритмов. Для случая программы сжатия файлов абстрактный базовый класс Compression этой иерархии объявляет интерфейс, общий для всех алгоритмов и используемый классом Compressor. Подклассы ZIP_Compression, ARJ_Compression и RAR_Compression его реализуют в соответствии с тем или иным алгоритмом. Класс Compressor содержит указатель на объект абстрактного типа Compression и предназначен для переадресации пользовательских запросов конкретному алгоритму. Для замены одного алгоритма другим достаточно перенастроить этот указатель на объект нужного типа.
Структура паттерна Strategy
UML-диаграмма классов паттерна Strategy
Реализация паттерна Strategy
Приведем реализацию приложения для сжатия файлов, спроектированного с применением паттерна Strategy.
#include #include // Иерархия классов, определяющая алгоритмы сжатия файлов class Compression < public: virtual ~Compression() <>virtual void compress( const string >; class ZIP_Compression : public Compression < public: void compress( const string cout >; class ARJ_Compression : public Compression < public: void compress( const string cout >; class RAR_Compression : public Compression < public: void compress( const string cout >; // Класс для использования class Compressor < public: Compressor( Compression* comp): p(comp) <>~Compressor() < delete p; >void compress( const string p->compress( file); > private: Compression* p; >; int main() < Compressor* p = new Compressor( new ZIP_Compression); p->compress( «file.txt»); delete p; return 0; >
Результаты применения паттерна Strategy
Достоинства паттерна Strategy
- Систему проще поддерживать и модифицировать, так как семейство алгоритмов перенесено в отдельную иерархию классов.
- Паттерн Strategy предоставляет возможность замены одного алгоритма другим в процессе выполнения программы.
- Паттерн Strategy позволяет скрыть детали реализации алгоритмов от клиента.
Недостатки паттерна Strategy
- Для правильной настройки системы пользователь должен знать об особенностях всех алгоритмов.
- Число классов в системе, построенной с применением паттерна Strategy, возрастает.
Источник: cpp-reference.ru