Патерн «Стратегія» (Strategy) визначає набір алгоритмів, інкапсулює кожен із них та забезпечує їхню взаємозамінність. У залежності від ситуації ми легко можемо замінити один алгоритм іншим — при цьому об’єкт, який його використовує, не має знати про конкретну реалізацію.
Коли застосовувати Стратегію?
Формально патерн «Стратегія» можна відобразити через UML‑схему:

Є інтерфейс IStrategy, що визначає метод Execute(), реалізований у конкретних стратегіях ConcreteStrategy1 і ConcreteStrategy2, кожна з яких виконує Execute() по‑своєму.
using System;
// Інтерфейс стратегії
public interface IStrategy
{
void Execute();
}
// Конкретна стратегія 1
public class ConcreteStrategy1 : IStrategy
{
public void Execute()
{
Console.WriteLine("Виконання стратегії 1");
}
}
// Конкретна стратегія 2
public class ConcreteStrategy2 : IStrategy
{
public void Execute()
{
Console.WriteLine("Виконання стратегії 2");
}
}
Клас Context містить поточну стратегію, має методи для її зміни та виконання.
using System;// Контекст, який використовує стратегію
public class Context
{
private IStrategy _strategy;
// Конструктор з встановленням початкової стратегії
public Context(IStrategy strategy)
{
_strategy = strategy;
}
// Метод для зміни стратегії під час виконання
public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}
// Метод, який виконує обрану стратегію
public void ExecuteStrategy()
{
_strategy.Execute();
}
}
У Main створюється контекст зі стратегією №1, виконується вона, потім замінюється на стратегію №2 і виконується знову.
class Program
{
static void Main(string[] args)
{
// Створення контексту з першою стратегією
Context context = new Context(new ConcreteStrategy1());
// Виконання стратегії
context.ExecuteStrategy();
// Зміна стратегії на другу
context.SetStrategy(new ConcreteStrategy2());
// Виконання стратегії
context.ExecuteStrategy();
}
}

В C# іноді використовують @base як ім’я змінної, щоб уникнути конфліктів із ключовим словом base. У прикладі змінна @base означає основу трикутника.
Створюється інтерфейс IShapeAreaCalculator з методом CalculateArea(), а потім реалізується три конкретні стратегії для кола, прямокутника і трикутника.
using System;
// Інтерфейс стратегії для обчислення площі фігури
public interface IShapeAreaCalculator
{
double CalculateArea();
}
// Конкретна стратегія для обчислення площі круга
public class CircleAreaCalculator : IShapeAreaCalculator
{
private double _radius;
public CircleAreaCalculator(double radius)
{
_radius = radius;
}
public double CalculateArea()
{
return Math.PI * _radius * _radius;
}
}
// Конкретна стратегія для обчислення площі прямокутника
public class RectangleAreaCalculator : IShapeAreaCalculator
{
private double _width;
private double _height;
public RectangleAreaCalculator(double width, double height)
{
_width = width;
_height = height;
}
public double CalculateArea()
{
return _width * _height;
}
}
// Конкретна стратегія для обчислення площі трикутника
public class TriangleAreaCalculator : IShapeAreaCalculator
{
private double _base;
private double _height;
public TriangleAreaCalculator(double @base, double height)
{
_base = @base;
_height = height;
}
public double CalculateArea()
{
return 0.5 * _base * _height;
}
}
У Main створюються екземпляри стратегій, викликається CalculateArea() для кожної фігури.
class Program
{
static void Main(string[] args)
{
// Створюємо різні фігури
IShapeAreaCalculator circleCalculator = new CircleAreaCalculator(5);
IShapeAreaCalculator rectangleCalculator = new RectangleAreaCalculator(4, 6);
IShapeAreaCalculator triangleCalculator = new TriangleAreaCalculator(3, 8);
// Обчислюємо площі фігур та виводимо результати
Console.WriteLine("Площа круга: " + circleCalculator.CalculateArea());
Console.WriteLine("Площа прямокутника: " + rectangleCalculator.CalculateArea());
Console.WriteLine("Площа трикутника: " + triangleCalculator.CalculateArea());
}
}

Створити модель ставка з качками різних видів.
Визначається суперклас Duck, а на його основі — підкласи конкретних качок.

Компанія вирішила додати всім качкам здатність літати.

У результаті по екрану летіли навіть гумові качки — метод fly() був не підходить всім підкласам. Додана поведінка виявилась непридатною для частини класів.

Потім додали качок, які не літають і не крякають.

Унаслідок цього стало зрозуміло, що спадкування не вирішує проблему — адже продукт оновлюється щопівроку, і доводиться переозначувати fly() і quack() для кожного нового класу.
Нова ідея:
IQuackable, бо крякати не всі качки;fly() з суперкласу Duck; визначити інтерфейс IFlyable з методом fly();IFlyable та мають метод fly().Відділяємо змінні компоненти, делегуючи поведінку з Duck:




У новій структурі є принципова особливість: клас Duck тепер делегує аспекти поведінки через спеціальні класи, а не реалізує їх безпосередньо.
Ось як це відбувається:
IFlyBehavior flyBehavior та IQuackBehavior quackBehavior, оголошені як інтерфейси. На етапі виконання кожному об’єкту присвоюється конкретна реалізація поведінки (наприклад, FlyWithWings або Squeak).fly() та quack() видаляються з Duck і підкласів, оскільки реалізація переміщується до відповідних класів поведінки. У Duck замість них використовуються performFly() та performQuack().
