Ответ
Паттерн Command инкапсулирует запрос как объект, что позволяет параметризовать клиентов с различными запросами, ставить запросы в очередь, логировать их, а также поддерживать отмену операций (Undo).
Реализация на C# с поддержкой отмены:
// 1. Абстрактная команда
public interface ICommand
{
void Execute();
void Undo();
}
// 2. Получатель команды — объект, над которым выполняется операция.
public class TextEditor
{
public string Text { get; set; } = string.Empty;
}
// 3. Конкретная команда для изменения текста
public class ChangeTextCommand : ICommand
{
private readonly TextEditor _editor;
private string _previousText;
private readonly string _newText;
public ChangeTextCommand(TextEditor editor, string newText)
{
_editor = editor;
_newText = newText;
}
public void Execute()
{
// Перед выполнением сохраняем состояние для возможной отмены
_previousText = _editor.Text;
_editor.Text = _newText;
Console.WriteLine($"Execute: Text changed to '{_newText}'.");
}
public void Undo()
{
// Восстанавливаем предыдущее состояние
_editor.Text = _previousText;
Console.WriteLine($"Undo: Text reverted to '{_previousText}'.");
}
}
// 4. Инициатор команд (Invoker) — управляет выполнением и историей.
public class CommandManager
{
private readonly Stack<ICommand> _undoStack = new Stack<ICommand>();
public void ExecuteCommand(ICommand command)
{
command.Execute();
_undoStack.Push(command); // Добавляем команду в историю для отмены
}
public void Undo()
{
if (_undoStack.Count > 0)
{
ICommand lastCommand = _undoStack.Pop();
lastCommand.Undo();
}
else
{
Console.WriteLine("Nothing to undo.");
}
}
}
// Пример использования
class Program
{
static void Main()
{
var editor = new TextEditor();
var manager = new CommandManager();
// Выполняем команды
manager.ExecuteCommand(new ChangeTextCommand(editor, "Hello"));
manager.ExecuteCommand(new ChangeTextCommand(editor, "Hello, World!"));
manager.ExecuteCommand(new ChangeTextCommand(editor, "Hello, Universe!"));
Console.WriteLine($"nCurrent text: {editor.Text}"); // Hello, Universe!
// Отменяем действия
manager.Undo(); // Возврат к "Hello, World!"
Console.WriteLine($"After undo: {editor.Text}");
manager.Undo(); // Возврат к "Hello"
Console.WriteLine($"After second undo: {editor.Text}");
}
}
Ключевые преимущества подхода:
- Разделение ответственности: Код, инициирующий действие (
CommandManager), не зависит от кода, который его выполняет (ChangeTextCommand) и от получателя (TextEditor). - Реализация сложных операций: Команда может объединять несколько действий над разными объектами, а её метод
Undo()корректно откатывает их все. - Расширяемость: Легко добавить новые типы команд, логирование, повторное выполнение (Redo) или отложенный запуск команд.