Паттерн Стратегия (Strategy) представляет шаблон проектирования, который определяет набор алгоритмов, инкапсулирует каждый из них и обеспечивает их взаимозаменяемость. В зависимости от ситуации мы можем легко заменить один используемый алгоритм другим. При этом замена алгоритма происходит независимо от объекта, который использует данный алгоритм.
Когда есть несколько родственных классов, которые отличаются поведением. Можно задать один основной класс, а разные варианты поведения вынести в отдельные классы и при необходимости их применять
Когда необходимо обеспечить выбор из нескольких вариантов алгоритмов, которые можно легко менять в зависимости от условий
Когда необходимо менять поведение объектов на стадии выполнения программы
Когда класс, применяющий определенную функциональность, ничего не должен знать о ее реализации
Формально паттерн Стратегия можно выразить следующей схемой UML:
Формальное определение паттерна на языке 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.