Створити модель ставка, в якому плавають і крякають качки різних видів.
Виділяємо змінні компоненти та інкапсулюємо їх, щоб у майбутньому можна було змінювати або розширювати без впливу на сталу частину.
Патерн «Стратегія» визначає сімейство алгоритмів, інкапсулює кожен з них та забезпечує їх взаємозамінність. Стратегія дозволяє змінювати алгоритми незалежно від клієнтів, які ними користуються.
Шаблон "Стратегія" - Демонстрація з качками
Ця демонстрація показує, як шаблон "Стратегія" дозволяє динамічно змінювати поведінку об'єктів.
Обрати тип качки:
Змінити поведінку:
Поведінка польоту:
Поведінка крякання:
Оберіть тип качки та натисніть "Продемонструвати поведінку"
Відділяємо змінні компоненти
Щоб відокремити «змінне від сталого», ми створюємо два набори класів (зовсім незалежних від Duck): один — для fly, інший — для quack. Кожен набір містить реалізацію відповідної поведінки.
Ми знаємо, що fly() і quack() — це частини класу Duck, які змінюються в залежності від підкласу.
Щоб відділити ці аспекти поведінки від класу Duck, ми винесли обидва методи з класу Duck та створили новий набір класів для представлення кожного з аспектів.
Створюємо інтерфейс типу польоту — IFlyBehavior
// Інтерфейс стратегії польоту.
// Кожна конкретна стратегія польоту має реалізувати цей інтерфейс.
public interface IFlyBehavior
{
// Метод для виконання польоту (повертає текстове описання дії)
object Fly();
}
Створюємо інтерфейс типу звуку — IQuackBehavior
// Інтерфейс стратегії крякання.
public interface IQuackBehavior
{
///
/// Генерує звук крякання (повертає рядок зі звуком)
///
string Quack();
}
Створюємо класи, які моделюють різні види польоту:
FlyNoWay — качки, що не літають.
// Стратегія "не літати" (для гумових або дерев'яних качок)
public class FlyNoWay : IFlyBehavior
{
public object Fly()
{
return "Я не літаю. 🦆";
}
}
FlyRocketPowered — реактивні моделі качок.
// Стратегія "реактивний політ" (для модельних качок з ракетним двигуном)
public class FlyRocketPowered : IFlyBehavior
{
public object Fly()
{
return "Я лечу на реактивній тязі! 🚀";
}
}
FlyWithWings — реальні качки.
// Стратегія польоту з крилами (для справжніх качок)
public class FlyWithWings : IFlyBehavior
{
public object Fly()
{
return "Я літаю, махаючи крилами! 🦆✈️";
}
}
Створюємо класи, які моделюють різні звуки:
MuteQuack — для дерев’яних або модельних качок.
// Стратегія "мовчання" (для дерев'яних або модельних качок)
public class MuteQuack : IQuackBehavior
{
public string Quack()
{
return "(ніякого звуку) 🦆";
}
}
Quack — для реальних качок.
// Стратегія звичайного крякання (для справжніх качок)
public class Quack : IQuackBehavior
{
public string Quack()
{
return "Кря-кря! 🦆";
}
}
Squeak — для гумових качок.
// Стратегія пищання (для гумових качок-іграшок)
public class Squeak : IQuackBehavior
{
// Масив можливих писклявих звуків
private static readonly string[] sounds = { "Піп!", "Пууп!", "Сквік!" };
private readonly Random _random = new Random();
public string Quack()
{
// Випадковий вибір одного з писклявих звуків
return sounds[_random.Next(sounds.Length)];
}
}
Отже, цього разу інтерфейси реалізуються не в класах Duck. Замість цього ми створюємо набір класів, єдиним сенсом яких є представлення певної поведінки.
Інтеграція поведінки з класом Duck
І тепер інтерфейс поведінки реалізує клас поведінки, а не клас Duck.
Підкласи Duck використовують поведінку, представлену через інтерфейс (IFlyBehavior або IQuackBehavior), тому фактична реалізація цієї поведінки (конкретні класи, що реалізують ці інтерфейси) не прив’язана до конкретного підкласу Duck.
Ось як це реалізується:
У класі Duck додаються дві змінні екземпляру: IFlyBehavior та IQuackBehavior, оголошені як типи інтерфейсів, а не конкретні класи. На етапі виконання кожен об’єкт присвоює цим змінним поліморфні реалізації поведінки (наприклад, FlyWithWings, Squeak тощо).
// Абстрактний базовий клас Duck, який використовує патерн "Стратегія"
public abstract class Duck
{
// Поля для зберігання поточних стратегій поведінки (політ і крякання)
protected IFlyBehavior flyBehavior; // стратегія польоту
protected IQuackBehavior quackBehavior; // стратегія крякання
// Властивості для отримання та динамічної заміни стратегій
public IQuackBehavior QuackBehavior
{
get => quackBehavior;
set => quackBehavior = value;
}
public IFlyBehavior FlyBehavior
{
get => flyBehavior;
set => flyBehavior = value;
}
// Абстрактний метод для відображення зовнішнього вигляду качки (реалізується в підкласах)
public abstract object Display();
// Делегування виконання поведінки поточним стратегіям
public object PerformFly() => flyBehavior.Fly();
public string PerformQuack() => quackBehavior.Quack();
// Загальна здатність плавати для всіх качок
public string Swim => "Всі качки вміють плавати, навіть іграшкові! 🦆🏊";
}
Методи fly() та quack() видаляються з класу Duck (і всіх підкласів), тому що ця поведінка переноситься до класів реалізації інтерфейсів. У класі Duck замість них з’являються методи performFly() та performQuack().
MallardDuck — реальна качка.
// Конкретна качка-кряква
public class MallardDuck : Duck
{
public MallardDuck()
{
// При створенні призначаємо природні стратегії
quackBehavior = new Quack(); // крякає звичайним чином
flyBehavior = new FlyWithWings(); // літає за допомогою крил
}
// Повертає опис зовнішнього вигляду
public override object Display => "Я справжня качка-кряква! 🦆🌿";
}
ModelDuck — модель качки.
// Модельна качка (іграшка)
public class ModelDuck : Duck
{
public ModelDuck()
{
// У модельної качки реактивний політ і відсутність звуку
flyBehavior = new FlyRocketPowered();
quackBehavior = new MuteQuack();
}
public override object Display => "Я модельна качка-іграшка 🦆";
}
RubberDuck — гумова качка.
// Гумова качка-іграшка
public class RubberDuck : Duck
{
public RubberDuck()
{
// Гумова качка не літає, але пищить
flyBehavior = new FlyNoWay();
quackBehavior = new Squeak();
}
public override object Display => "Я гумова качка-іграшка 🦆🎈";
}
Зрештою, створюємо ставок із різними качками і викликаємо їхні методи для демонстрації різної поведінки.
using System;
class Program
{
static void Main(string[] args)
{
// Створюємо масив різних качок
Duck[] ducks = { new MallardDuck(), new ModelDuck(), new RubberDuck() };
// Демонструємо поведінку кожної качки
foreach (Duck duck in ducks)
{
Console.WriteLine("=== Демонстрація качки ===");
Console.WriteLine("Зовнішній вигляд: " + duck.Display);
Console.WriteLine("Плавання: " + duck.Swim);
Console.WriteLine("Звук: " + duck.PerformQuack());
Console.WriteLine("Політ: " + duck.PerformFly());
Console.WriteLine();
}
// Демонстрація динамічної зміни стратегії
Duck model = new ModelDuck();
Console.WriteLine("Початковий політ: " + model.PerformFly());
model.FlyBehavior = new FlyWithWings(); // змінюємо стратегію на льоту
Console.WriteLine("Новий політ: " + model.PerformFly());
Console.ReadKey();
}
}
Програмна реалізація стратегії (Strategy)
using System;
// ========== ІНТЕРФЕЙСИ СТРАТЕГІЙ ==========
// Інтерфейс стратегії польоту
public interface IFlyBehavior
{
object Fly();
}
// Інтерфейс стратегії крякання
public interface IQuackBehavior
{
string Quack();
}
// ========== КОНКРЕТНІ СТРАТЕГІЇ ПОЛЬОТУ ==========
// Політ з крилами
public class FlyWithWings : IFlyBehavior
{
public object Fly() => "Я літаю, махаючи крилами! 🦆✈️";
}
// Не літати
public class FlyNoWay : IFlyBehavior
{
public object Fly() => "Я не літаю. 🦆";
}
// Реактивний політ
public class FlyRocketPowered : IFlyBehavior
{
public object Fly() => "Я лечу на реактивній тязі! 🚀";
}
// ========== КОНКРЕТНІ СТРАТЕГІЇ КРЯКАННЯ ==========
// Звичайне крякання
public class Quack : IQuackBehavior
{
public string Quack() => "Кря-кря! 🦆";
}
// Мовчання
public class MuteQuack : IQuackBehavior
{
public string Quack() => "(ніякого звуку) 🦆";
}
// Пищання (для гумових качок)
public class Squeak : IQuackBehavior
{
public string Quack() => "Піп-піп! 🦆🔊";
}
// ========== БАЗОВИЙ КЛАС DUCK ==========
public abstract class Duck
{
// Стратегії поведінки (композиція)
protected IFlyBehavior flyBehavior;
protected IQuackBehavior quackBehavior;
// Властивості для отримання/заміни стратегій
public IQuackBehavior QuackBehavior
{
get => quackBehavior;
set => quackBehavior = value;
}
public IFlyBehavior FlyBehavior
{
get => flyBehavior;
set => flyBehavior = value;
}
// Відображення зовнішнього вигляду (має бути реалізовано в підкласах)
public abstract object Display { get; }
// Делегування виконання поведінки стратегіям
public object PerformFly() => flyBehavior.Fly();
public string PerformQuack() => quackBehavior.Quack();
// Загальна здатність плавати
public string Swim => "Всі качки вміють плавати, навіть іграшкові! 🦆🏊";
}
// ========== КОНКРЕТНІ КАЧКИ ==========
// Справжня кряква
public class MallardDuck : Duck
{
public MallardDuck()
{
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public override object Display => "Я справжня качка-кряква! 🦆🌿";
}
// Модельна качка (іграшка)
public class ModelDuck : Duck
{
public ModelDuck()
{
flyBehavior = new FlyRocketPowered();
quackBehavior = new MuteQuack();
}
public override object Display => "Я модельна качка-іграшка 🦆";
}
// Гумова качка
public class RubberDuck : Duck
{
public RubberDuck()
{
flyBehavior = new FlyNoWay();
quackBehavior = new Squeak();
}
public override object Display => "Я гумова качка-іграшка 🦆🎈";
}
// ========== ГОЛОВНА ПРОГРАМА ==========
class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
// Створюємо масив качок
Duck[] ducks = { new MallardDuck(), new ModelDuck(), new RubberDuck() };
// Демонструємо поведінку кожної качки
foreach (Duck duck in ducks)
{
Console.WriteLine("=== Демонстрація качки ===");
Console.WriteLine("Зовнішній вигляд: " + duck.Display);
Console.WriteLine("Плавання: " + duck.Swim);
Console.WriteLine("Звук: " + duck.PerformQuack());
Console.WriteLine("Політ: " + duck.PerformFly());
Console.WriteLine();
}
// Демонстрація динамічної зміни стратегії
Console.WriteLine("\n=== Експеримент: Модельна качка тепер літає з крилами ===");
Duck model = new ModelDuck();
Console.WriteLine("Початковий політ: " + model.PerformFly());
model.FlyBehavior = new FlyWithWings(); // замінюємо стратегію
Console.WriteLine("Новий політ: " + model.PerformFly());
Console.ReadKey();
}
}
🔴 Качка-істеричка - Повний код з виділенням змін
Демонстрація розширення патерну "Стратегія"
📋 Огляд змін у коді:
Додано новий інтерфейс ISwimBehavior для стратегій плавання
Додано дві нові стратегії плавання: SwimOnSurface та SwimUnderwater
Додано нову стратегію польоту DiveIntoWater (пірнання)
Додано нову стратегію крякання HystericalScream (істеричний крик)
Оновлено базовий клас Duck для підтримки нової поведінки плавання
Додано новий клас HystericalDuck - качка-істеричка
Оновлено метод Main для тестування нової качки
1. Інтерфейси поведінки (без змін)
// Інтерфейс для польоту (без змін)
public interface IFlyBehavior
{
object Fly();
}
// Інтерфейс для крякання (без змін)
public interface IQuackBehavior
{
string Quacking();
}
2. НОВИЙ інтерфейс для плавання
// 🔴 НОВИЙ ІНТЕРФЕЙС: Додано для підтримки різних стратегій плавання
public interface ISwimBehavior
{
string Swim();
}
3. Стратегії польоту (існуючі + нова)
// Реалізація: качка літає з крилами (без змін)
public class FlyWithWings : IFlyBehavior
{
public object Fly() => "Я лечу, розмахуючи крилами! 🕊️";
}
// Реалізація: качка не літає (без змін)
public class FlyNoWay : IFlyBehavior
{
public object Fly() => "Я не можу літати... 😔";
}
// Реалізація: качка з реактивним двигуном (без змін)
public class FlyRocketPowered : IFlyBehavior
{
public object Fly() => "Я лечу на реактивному двигуні! 🚀🦆";
}
// 🔴 НОВА СТРАТЕГІЯ: Додано для качки-істерички
public class DiveIntoWater : IFlyBehavior
{
public object Fly() => "Я не літаю, а пірнаю у воду! 💦🦆";
}
4. Стратегії крякання (існуючі + нова)
// Реалізація: звичайне крякання (без змін)
public class Quack : IQuackBehavior
{
public string Quacking() => "Кря-кря! 🦆";
}
// Реалізація: іграшкова качка пищить (без змін)
public class Squeak : IQuackBehavior
{
public string Quacking() => "Пі-пі! 🔊";
}
// Реалізація: качка мовчить (без змін)
public class MuteQuack : IQuackBehavior
{
public string Quacking() => " ... (мовчу) 🤫";
}
// 🔴 НОВА СТРАТЕГІЯ: Додано для качки-істерички
public class HystericalScream : IQuackBehavior
{
public string Quacking() => "ААААА! ПАНІКА! КАТАСТРОФА! 😱";
}
5. НОВІ стратегії плавання
// 🔴 НОВІ СТРАТЕГІЇ: Додано для підтримки різних типів плавання
public class SwimOnSurface : ISwimBehavior
{
public string Swim() => "Я плаваю на поверхні води! 🦆🏊";
}
public class SwimUnderwater : ISwimBehavior
{
public string Swim() => "Я плаваю тільки під водою! 🌊🤿";
}
6. Базовий клас качки (оновлено)
// Базовий клас качки
public abstract class Duck
{
// Посилання на об'єкти поведінки
protected IFlyBehavior flyBehavior;
protected IQuackBehavior quackBehavior;
// 🔴 ДОДАНО: Нова поведінка плавання
protected ISwimBehavior swimBehavior;
// Властивості для зміни поведінки під час виконання
public IQuackBehavior QuackBehavior
{
get => quackBehavior;
set => quackBehavior = value;
}
public IFlyBehavior FlyBehavior
{
get => flyBehavior;
set => flyBehavior = value;
}
// 🔴 ДОДАНО: Нова властивість для плавання
public ISwimBehavior SwimBehavior
{
get => swimBehavior;
set => swimBehavior = value;
}
public abstract object Display { get; }
// Методи для виконання поведінки
public object PerformFly() => flyBehavior.Fly();
public string PerformQuack() => quackBehavior.Quacking();
// 🔴 ДОДАНО: Новий метод для плавання
public string PerformSwim() => swimBehavior.Swim();
// 🔴 ВИДАЛЕНО: Стара реалізація плавання (замінена на стратегію)// public string Swim => "Всі качки вміють плавати, навіть іграшкові! 🦆🏊";
}
7. Конкретні класи качок (існуючі + нова)
// Дика качка (оновлено для нової поведінки плавання)
public class MallardDuck : Duck
{
public MallardDuck()
{
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
// 🔴 ОНОВЛЕНО: Додано ініціалізацію плавання
swimBehavior = new SwimOnSurface();
}
public override object Display => "Я дика качка з зеленим пір'ям! 🌿🦆";
}
// Іграшкова качка (оновлено для нової поведінки плавання)
public class RubberDuck : Duck
{
public RubberDuck()
{
flyBehavior = new FlyNoWay();
quackBehavior = new Squeak();
// 🔴 ОНОВЛЕНО: Додано ініціалізацію плавання
swimBehavior = new SwimOnSurface();
}
public override object Display => "Я жовта гумова качка! 🟡🦆";
}
// Качка-приманка (оновлено для нової поведінки плавання)
public class DecoyDuck : Duck
{
public DecoyDuck()
{
flyBehavior = new FlyNoWay();
quackBehavior = new MuteQuack();
// 🔴 ОНОВЛЕНО: Додано ініціалізацію плавання
swimBehavior = new SwimOnSurface();
}
public override object Display => "Я качка-приманка, просто манекен 🎯🦆";
}
// 🔴 НОВИЙ КЛАС: Додано качку-істеричку
public class HystericalDuck : Duck
{
public HystericalDuck()
{
// 🔴 ВИКОРИСТАНО: Нова стратегія польоту (пірнання)
flyBehavior = new DiveIntoWater();
// 🔴 ВИКОРИСТАНО: Нова стратегія крякання (істеричний крик)
quackBehavior = new HystericalScream();
// 🔴 ВИКОРИСТАНО: Нова стратегія плавання (під водою)
swimBehavior = new SwimUnderwater();
}
public override object Display => "Я качка-істеричка! Все мене дратує! 💢🦆";
}
8. Метод Main (оновлено)
class Program
{
static void Main()
{
// Тестування існуючих качок (оновлено)
Duck mallard = new MallardDuck();
Console.WriteLine(mallard.Display);
Console.WriteLine(mallard.PerformFly());
Console.WriteLine(mallard.PerformQuack());
// 🔴 ОНОВЛЕНО: Замінено на новий метод плавання
Console.WriteLine(mallard.PerformSwim());
Console.WriteLine();
Duck rubber = new RubberDuck();
Console.WriteLine(rubber.Display);
Console.WriteLine(rubber.PerformFly());
Console.WriteLine(rubber.PerformQuack());
// 🔴 ОНОВЛЕНО: Замінено на новий метод плавання
Console.WriteLine(rubber.PerformSwim());
Console.WriteLine();
// 🔴 ДОДАНО: Тестування нової качки-істерички
Duck hysterical = new HystericalDuck();
Console.WriteLine(hysterical.Display);
Console.WriteLine(hysterical.PerformFly());
Console.WriteLine(hysterical.PerformQuack());
Console.WriteLine(hysterical.PerformSwim());
Console.WriteLine();
// Динамічна зміна поведінки під час виконання
rubber.FlyBehavior = new FlyRocketPowered();
Console.WriteLine("Зміна поведінки польоту гумової качки: ");
Console.WriteLine(rubber.PerformFly());
// 🔴 ДОДАНО: Динамічна зміна поведінки качки-істерички
hysterical.QuackBehavior = new Squeak();
Console.WriteLine("Качка-істеричка заспокоїлась і тепер пищить: ");
Console.WriteLine(hysterical.PerformQuack());
}
}
Результат виконання програми:
Я дика качка з зеленим пір'ям! 🌿🦆
Я лечу, розмахуючи крилами! 🕊️
Кря-кря! 🦆
Я плаваю на поверхні води! 🦆🏊
Я жовта гумова качка! 🟡🦆
Я не можу літати... 😔
Пі-пі! 🔊
Я плаваю на поверхні води! 🦆🏊
Я качка-істеричка! Все мене дратує! 💢🦆
Я не літаю, а пірнаю у воду! 💦🦆
ААААА! ПАНІКА! КАТАСТРОФА! 😱
Я плаваю тільки під водою! 🌊🤿
Зміна поведінки польоту гумової качки:
Я лечу на реактивному двигуні! 🚀🦆
Качка-істеричка заспокоїлась і тепер пищить:
Пі-пі! 🔊