Паттерн Команда
Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 56 из 70.
Паттерн Команда — это поведенческий паттерн, который превращает запрос в самостоятельный объект, содержащий всю информацию о запросе. Такая трансформация позволяет передавать запросы в качестве аргументов методов, ставить их в очередь, логировать, а также поддерживать отмену операций.
Паттерн отделяет объект, инициирующий операцию, от объекта, который знает, как её выполнить. Он состоит из четырех ключевых компонентов: интерфейса команды, конкретных команд, получателя (объекта, который выполняет фактическую работу) и инициатора (объекта, который запускает команды).
public interface ICommand
{
void Execute();
}
public class Light // Получатель
{
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}
public class TurnOnCommand : ICommand
{
private Light _light;
public TurnOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
}
public class TurnOffCommand : ICommand
{
private Light _light;
public TurnOffCommand(Light light) => _light = light;
public void Execute() => _light.TurnOff();
}Инициатор (invoker) сохраняет и выполняет команды, не зная, что именно они делают:
public class RemoteControl // Инициатор (Invoker)
{
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command.Execute();
}
// Использование
var light = new Light();
var remote = new RemoteControl();
remote.SetCommand(new TurnOnCommand(light));
remote.PressButton(); // Свет ВКЛЮЧЕН
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // Свет ВЫКЛЮЧЕНПаттерн Команда особенно эффективен для реализации функциональности отмены, записи макросов или постановки операций в очередь для последующего выполнения. Каждая команда инкапсулирует всё необходимое для выполнения действия, делая вашу систему более гибкой и расширяемой.
Задание
ЛегкоДавайте создадим командную систему текстового редактора, используя паттерн «Команда» (Command). Вы создадите систему, в которой операции редактирования, такие как вставка и удаление текста, инкапсулированы в объекты команд, что позволяет редактору выполнять их через единый интерфейс.
Вы организуете свой код в трех файлах:
Command.cs: Определите интерфейсICommandв пространстве именTextEditorс единственным методомExecute(). Затем создайте два конкретных класса команд:InsertCommand— принимаетDocumentи строкуtextв своем конструкторе. При выполнении он добавляет текст в документ и выводитInserted: {text}DeleteCommand— принимаетDocumentиint countв своем конструкторе. При выполнении он удаляет последниеcountсимволов из документа и выводитDeleted: {count} characters
Document.cs: Создайте классDocumentв том же пространстве имен. Это ваш получатель (receiver) — объект, который непосредственно выполняет операции с текстом. Он должен иметь:- Приватное строковое поле для хранения содержимого (инициализируйте пустой строкой)
- Метод
Append(string text), который добавляет текст к содержимому - Метод
RemoveLast(int count), который удаляет последниеcountсимволов (если count превышает длину содержимого, очистите все содержимое) - Метод
GetContent(), который возвращает текущее содержимое
Program.cs: Создайте классEditor, который выступает в роли инициатора (invoker). Он должен иметь методSetCommand(ICommand command)и методExecuteCommand(), который запускает текущую команду. В основном коде продемонстрируйте работу паттерна «Команда», обработав серию операций редактирования.
Вы получите следующие входные данные:
- Количество команд для выполнения
- Для каждой команды: тип команды (
insertилиdelete), за которым следует либо текст для вставки, либо количество символов для удаления
После выполнения всех команд выведите финальное содержимое документа на новой строке в формате Content: {content}.
Например, если входные данные следующие:
4
insert
Hello
insert
World
delete
3
insert
!Вывод должен быть таким:
Inserted: Hello
Inserted: World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!Обратите внимание, что Editor (инициатор) ничего не знает о манипуляциях с текстом — он просто выполняет любую команду, которую ему дают. Команды инкапсулируют как действие, так и получателя, что позволяет легко добавлять новые операции, такие как ReplaceCommand, не меняя класс редактора!
Шпаргалка
Паттерн Команда (Command) — это поведенческий паттерн, который инкапсулирует запрос в виде отдельного объекта, содержащего всю информацию о запросе. Это позволяет передавать запросы в качестве аргументов методов, ставить их в очередь, логировать или поддерживать операции отмены.
Паттерн состоит из четырех ключевых компонентов:
- Интерфейс команды (Command interface) — определяет метод Execute()
- Конкретные команды (Concrete commands) — реализуют интерфейс команды и инкапсулируют конкретные действия
- Получатель (Receiver) — объект, который выполняет фактическую работу
- Инициатор (Invoker) — объект, который инициирует выполнение команд, не зная об их реализации
Интерфейс команды и конкретные команды
public interface ICommand
{
void Execute();
}
public class TurnOnCommand : ICommand
{
private Light _light;
public TurnOnCommand(Light light) => _light = light;
public void Execute() => _light.TurnOn();
}
public class TurnOffCommand : ICommand
{
private Light _light;
public TurnOffCommand(Light light) => _light = light;
public void Execute() => _light.TurnOff();
}Получатель
public class Light
{
public void TurnOn() => Console.WriteLine("Light is ON");
public void TurnOff() => Console.WriteLine("Light is OFF");
}Инициатор
public class RemoteControl
{
private ICommand _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command.Execute();
}Использование
var light = new Light();
var remote = new RemoteControl();
remote.SetCommand(new TurnOnCommand(light));
remote.PressButton(); // Light is ON
remote.SetCommand(new TurnOffCommand(light));
remote.PressButton(); // Light is OFFПаттерн Команда полезен для реализации функционала отмены, записи макросов или постановки операций в очередь для последующего выполнения. Каждая команда инкапсулирует всё необходимое для выполнения действия, что делает системы более гибкими и расширяемыми.
Попробуйте сами
using System;
using TextEditor;
// TODO: Создайте класс Editor (инициатор/invoker)
public class Editor
{
// TODO: Добавьте приватное поле для хранения текущей команды
private ICommand _command;
// TODO: Реализуйте метод SetCommand
public void SetCommand(ICommand command)
{
// TODO: Сохраните команду
}
// TODO: Реализуйте метод ExecuteCommand
public void ExecuteCommand()
{
// TODO: Выполните текущую команду
}
}
class Program
{
public static void Main(string[] args)
{
// Считать количество команд
int n = Convert.ToInt32(Console.ReadLine());
// Создать документ (получатель/receiver)
Document document = new Document();
// Создать редактор (инициатор/invoker)
Editor editor = new Editor();
// TODO: Обработать каждую команду
for (int i = 0; i < n; i++)
{
string commandType = Console.ReadLine();
// TODO: На основе commandType создайте соответствующую команду
// - Если "insert": прочитать текст и создать InsertCommand
// - Если "delete": прочитать количество и создать DeleteCommand
// Затем установите команду в редакторе и выполните её
}
// TODO: Выведите итоговое содержимое в формате "Content: {content}"
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методов