“Патерн Вівторка” #19: Стратегія (Strategy)
|Просто як двері – якщо на дворі дощ, то ви берете парасольку і куртку, а якщо палить сонце – то ви берете футболку і сонцезахисні окуляри. Що одівати є вашою стратегією, яку ви міняєте в залежності від обставин. Але замість того, щоб дивитися за вікно яка погода а потім мандрувати до шафки із одягом і вибирати що надягнути а далі, пам’ятаючи яка погода, йти до іншої шафки і брати парасольку або окуляри, ви просто піднімаєтеся із ліжка протираєте очі і вам у руки жінка(чоловік, що маловірогідно) подає потрібний одяг і аксесуари. Іншими словами стратегія на сьогоднішній день просто була подана і ви нею скористалися.
СТРАТЕГІЯ
Стратегія – це дизайн патерн, що зберігає сім’ю алгоритмів і дозволяє їх міняти незалежно та переключатися між ними.
Припустимо що в класі Я (Myself) ми маємо метод піти на вулицю GoOutside() в якому ми вибираємо собі одяг і шуруємо на вулицю.
Метод міг виглядати подібно до ось цього:
public void GoOutside()
{
var weather = Weather.GetWeather();
string clothes = GetClothes(weather);
string accessories = GetAccessories(weather);
Console.WriteLine("Today I wore {0} and took {1}", clothes, accessories);
}
private string GetAccessories(string weather)
{
string accessories;
switch (weather)
{
case "sun":
accessories = "sunglasses";
break;
case "rain":
accessories = "umbrella";
break;
default:
accessories = "nothing";
break;
}
return accessories;
}
private string GetClothes(string weather)
{
string clothes;
switch (weather)
{
case "sun":
clothes = "T-Shirt";
break;
case "rain":
clothes = "Coat";
break;
default:
clothes = "Shirt";
break;
}
return clothes;
}
воно звичайно добре, але як тільки вам треба буде прилаштовуватися до випавшого снігу, вам прийдеться добавити ще один case в троьхсот місцях. Із одної сторони воно не складно, але із іншої вас може “дістати” або код із методом GoOutside() вже не можна буде міняти. Що тоді?
Тут я наводжу приклад із switch, тому що Strategy – це елегантний спосіб позбутися цього чуда.
internal class Myself
{
private IWearingStrategy _wearingStrategy = new DefaultWearingStrategy();
public void ChangeStrategy(IWearingStrategy wearingStrategy)
{
_wearingStrategy = wearingStrategy;
}
public void GoOutside()
{
var clothes = _wearingStrategy.GetClothes();
var accessories = _wearingStrategy.GetAccessories();
Console.WriteLine("Today I wore {0} and took {1}", clothes, accessories);
}
}
Як бачимо ми маємо інтерфейс стратегії із двома методами. Нумо глянемо як виглядає стратегія на сонячний день:
public interface IWearingStrategy
{
string GetClothes();
string GetAccessories();
}
class SunshineWearingStrategy : IWearingStrategy
{
public string GetClothes()
{
return "T-Shirt";
}
public string GetAccessories()
{
return "sunglasses";
}
}
Все, що нам залишилося то це правильно проставити стратегію. Ага! Всерівно хтось буде змішений проставити правильну стратегію (жінка, яка встала ранше і глянула у вікно, але вона може зробити це одним свічом, про який ви нічого не знаєте).
var me = new Myself();
me.ChangeStrategy(new RainWearingStrategy());
me.GoOutside();
Вивід простий: “Today I wore Coat and took umbrella”
Ще одним (але не моїм) гарним прикладом є зміна стратегії сортування списку в залежності від його розміру. Всі ми знаємо що для малої кількості даних достатньо сортування вставками або якийсь інший простий спосіб, для відносно великих списків найоптимальніше використовувати швидке сортування, а для дуже великої к-ті даних пірамідальне. Так от, алгоритм зберігається у своєму класі і в залежності від кількості елементів ми просто міняємо реалізацію алгоритму.