Створити модель ставка, в якому плавають і крякають качки різних видів.
Виділяємо змінні компоненти та інкапсулюємо їх, щоб у майбутньому можна було змінювати або розширювати без впливу на сталу частину.
Ця демонстрація показує, як шаблон "Стратегія" дозволяє динамічно змінювати поведінку об'єктів.
Щоб відокремити «змінне від сталого», ми створюємо два набори класів (зовсім незалежних від Duck): один — для fly, інший — для quack. Кожен набір містить реалізацію відповідної поведінки.
Ми знаємо, що fly() і quack() — це частини класу Duck, які змінюються в залежності від підкласу.
Щоб відділити ці аспекти поведінки від класу Duck, ми винесли обидва методи з класу Duck та створили новий набір класів для представлення кожного з аспектів.
Створюємо інтерфейс типу польоту — IFlyBehavior
public interface IFlyBehavior
{
void Fly(); // Метод для виконання польоту
bool CanFly { get; } // Властивість, що вказує на можливість польоту
}
Створюємо інтерфейс типу звуку — IQuackBehavior
public interface IQuackBehavior
{
///
/// Генерує звук крякання
///
/// Рядок, що представляє звук крякання
string Quack(); // Більш стандартна назва методу
}
Створюємо класи, які моделюють різні види польоту:
public class FlyNoWay : IFlyBehavior
{
public string FlyStatus => "Не літає";
public void Fly()
{
Console.WriteLine("Не можу літати");
}
}
// Клас, що реалізує реактивний політ (стратегія "з ракетним двигуном")
public class FlyRocketPowered : IFlyBehavior
{
public object Fly => "Я лечу на реактивній тязі! 🚀";
}
// Клас, що реалізує поведінку польоту з крилами (для справжньої качки-крякви)
public class FlyWithWings : IFlyBehavior
{
public object Fly => "Я літаю, махаючи крилами! 🦆✈️";
}
Створюємо класи, які моделюють різні звуки:
// Клас, що реалізує "мовчазне" крякання для дерев'яної качки
public class MuteQuack : IQuackBehavior
{
public string Quacking => "(ніякого звуку) 🦆";
}
// Клас, що реалізує справжнє крякання для качки-крякви
public class Quack : IQuackBehavior
{
public string Quacking => "Кря-кря! 🦆";
}
//Відповідає поведінці гумових качок-іграшок
public class Squeak : IQuackBehavior
{
private static readonly string[] sounds = { "Піп!", "Пууп!", "Сквік!" };
private readonly Random _random = new Random();
public string Quacking => sounds[_random.Next(sounds.Length)];
}
Отже, цього разу інтерфейси реалізуються не в класах 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 object PerformQuack() => quackBehavior.Quacking();
// Загальна поведінка для всіх качок
public string Swim => "Всі качки вміють плавати, навіть іграшкові! 🦆🏊";
}
fly() та quack() видаляються з класу Duck (і всіх підкласів), тому що ця поведінка переноситься до класів реалізації інтерфейсів. У класі Duck замість них з’являються методи performFly() та performQuack(). // Клас справжньої качки-крякви
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 => "Я модельна качка-іграшка 🦆";
}
Зрештою, створюємо ставок із різними качками і викликаємо їхні методи для демонстрації різної поведінки.
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();
}
Console.ReadKey();
}
}
/*
Приклад виводу:
=== Демонстрація качки ===
Зовнішній вигляд: Я справжня качка-кряква! 🦆🌿
Плавання: Всі качки вміють плавати, навіть іграшкові! 🦆🏊
Звук: Кря-кря! 🦆
Політ: Я літаю, махаючи крилами! 🦆✈️
=== Демонстрація качки ===
Зовнішній вигляд: Я модельна качка-іграшка 🦆
Плавання: Всі качки вміють плавати, навіть іграшкові! 🦆🏊
Звук: (ніякого звуку) 🦆
Політ: Я лечу на реактивній тязі! 🚀
=== Демонстрація качки ===
Зовнішній вигляд: Я гумова качка-іграшка 🦆🎈
Плавання: Всі качки вміють плавати, навіть іграшкові! 🦆🏊
Звук: Піп-піп! 🦆🔊
Політ: Я не літаю. 🦆
*/
using System;
// Інтерфейси поведінки
public interface IFlyBehavior
{
object Fly();
}
public interface IQuackBehavior
{
string Quacking();
}
// Реалізації поведінки польоту
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 Quacking() => "Кря-кря! 🦆";
}
public class MuteQuack : IQuackBehavior
{
public string Quacking() => "(ніякого звуку) 🦆";
}
public class Squeak : IQuackBehavior
{
public string Quacking() => "Піп-піп! 🦆🔊";
}
// Базовий клас качки
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.Quacking();
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();
}
}