Ответ
Команда (Command) — это поведенческий паттерн проектирования, который инкапсулирует запрос на выполнение действия в виде объекта. Это позволяет параметризовать клиентов различными запросами, ставить запросы в очередь, логировать их, а также поддерживать отмену операций.
Суть паттерна: Он превращает операцию в самостоятельный объект, содержащий всю информацию, необходимую для её выполнения. Это отделяет объект, инициирующий операцию (Invoker), от объекта, который знает, как её выполнить (Receiver).
Ключевые компоненты:
ICommand: Интерфейс, объявляющий метод выполнения (например,Execute()). Часто включает методUndo().ConcreteCommand: Конкретная реализация команды. Она связывает действие с Получателем (Receiver), реализуя методыExecute/Undo.Invoker: Инициатор команды. Он знает, когда команду нужно выполнить, но не знает, как она выполняется. Хранит ссылку на объект команды.Receiver: Объект, который знает, как выполнить само действие. Команда делегирует ему основную работу.Client: Создаёт объект конкретной команды и связывает его с получателем и инициатором.
Преимущества:
- Отделение отправителя от получателя: Отправитель (Invoker) не зависит от конкретных классов получателей.
- Реализация отмены/повтора (Undo/Redo): Легко реализуется, если команды сохраняют состояние, необходимое для отката.
- Очереди и планирование операций: Команды как объекты можно собирать в очередь, выполнять по расписанию или передавать по сети.
- Сбор сложных команд: Можно создавать макрокоманды (составные команды) из набора простых.
Пример на C# (Управление умным домом с поддержкой отмены):
// 1. Интерфейс команды
public interface ICommand
{
void Execute();
void Undo();
}
// 2. Получатель (Receiver) — знает, как выполнять действия
public class Light
{
private bool _isOn = false;
private string _location;
public Light(string location) => _location = location;
public void TurnOn()
{
_isOn = true;
Console.WriteLine($"Свет в {_location} ВКЛЮЧЕН.");
}
public void TurnOff()
{
_isOn = false;
Console.WriteLine($"Свет в {_location} ВЫКЛЮЧЕН.");
}
}
// 3. Конкретные команды
public class LightOnCommand : ICommand
{
private Light _light;
public LightOnCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn();
}
public void Undo()
{
_light.TurnOff(); // Отмена действия «включить» — это «выключить»
}
}
public class LightOffCommand : ICommand
{
private Light _light;
public LightOffCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOff();
}
public void Undo()
{
_light.TurnOn(); // Отмена действия «выключить» — это «включить»
}
}
// 4. Инициатор (Invoker) — пульт с одной кнопкой и поддержкой отмены
public class SimpleRemoteControl
{
private ICommand _slot;
private ICommand _lastCommand; // Для реализации отмены
public void SetCommand(ICommand command)
{
_slot = command;
}
public void ButtonWasPressed()
{
_slot?.Execute();
_lastCommand = _slot; // Запоминаем последнюю выполненную команду
}
public void UndoButtonWasPressed()
{
_lastCommand?.Undo();
_lastCommand = null;
}
}
// 5. Клиентский код
class Program
{
static void Main()
{
// Создаём получателя
Light livingRoomLight = new Light("Гостиной");
// Создаём команды, связывая их с получателем
ICommand lightOn = new LightOnCommand(livingRoomLight);
ICommand lightOff = new LightOffCommand(livingRoomLight);
// Создаём инициатор (пульт)
SimpleRemoteControl remote = new SimpleRemoteControl();
// Настраиваем пульт на команду «Включить свет»
remote.SetCommand(lightOn);
remote.ButtonWasPressed(); // Вывод: Свет в Гостиной ВКЛЮЧЕН.
// Настраиваем пульт на команду «Выключить свет»
remote.SetCommand(lightOff);
remote.ButtonWasPressed(); // Вывод: Свет в Гостиной ВЫКЛЮЧЕН.
// Демонстрация отмены: снова включим свет и отменим действие
remote.SetCommand(lightOn);
remote.ButtonWasPressed(); // Вывод: Свет в Гостиной ВКЛЮЧЕН.
remote.UndoButtonWasPressed(); // Вывод: Свет в Гостиной ВЫКЛЮЧЕН. (отмена)
}
} Ответ 18+ 🔞
А, паттерн Команда, ну это же классика, блядь! Прям как в старом анекдоте про пульт и тещу. Сейчас объясню на пальцах, только не ори, что сложно.
Представь, что ты царь и бог в своём умном доме. У тебя есть куча приборов — свет, чайник, телевизор. И есть пульт с кнопками. Так вот, паттерн Команда — это когда каждая кнопка на пульте это не просто «пиу-пиу, сделай что-то», а целый запечатанный приказ в конверте. Ты, как царь (клиент), создаёшь эти приказы: «Включить свет в гостиной», «Вскипятить чайник». Пульт (инициатор) даже не в курсе, что внутри конверта. Его дело — тыкнуть кнопку и отправить конверт адресату. А адресат (получатель) уже вскрывает конверт и делает, что там написано: лампочка зажигается, чайник булькает.
И вся магия в том, что ты можешь эти конверты-команды складывать в стопку, чтобы потом отменить (ой, бля, не туда нажал!) или вообще составить один жирный конверт «Сценарий „Вечерний дебош“», внутри которого «Включить свет, включить музыку, начать наливать».
Смотри, как это выглядит в коде, на примере этого самого пульта. Только не засыпай, я стараюсь.
// 1. Это как договор для всех команд. Любая команда должна уметь Выполниться и Отмениться.
public interface ICommand
{
void Execute();
void Undo();
}
// 2. А это получатель, тот самый прибор. Он реально знает, как надо жужжать и светиться.
public class Light
{
private bool _isOn = false;
private string _location;
public Light(string location) => _location = location;
public void TurnOn()
{
_isOn = true;
Console.WriteLine($"Свет в {_location} ВКЛЮЧЕН.");
}
public void TurnOff()
{
_isOn = false;
Console.WriteLine($"Свет в {_location} ВЫКЛЮЧЕН.");
}
}
// 3. А вот и сами команды. Конверты с инструкциями.
public class LightOnCommand : ICommand
{
private Light _light; // Адрес получателя написан тут
public LightOnCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOn(); // Вскрыли конверт, прочитали: "Включись!"
}
public void Undo()
{
_light.TurnOff(); // А отмена команды "включись" — это "выключись", логично же, ёпта!
}
}
// Команда "Выключить" — всё то же самое, только наоборот.
public class LightOffCommand : ICommand
{
private Light _light;
public LightOffCommand(Light light)
{
_light = light;
}
public void Execute()
{
_light.TurnOff();
}
public void Undo()
{
_light.TurnOn();
}
}
// 4. Пульт управления, инициатор. Тупая железяка, которая только кнопки жмёт.
public class SimpleRemoteControl
{
private ICommand _slot; // Сюда вставляется текущая команда-конверт
private ICommand _lastCommand; // А это чтобы помнить, что ты только что нажал, для отмены
public void SetCommand(ICommand command)
{
_slot = command; // Вставили в пульт конверт "Включить свет в гостиной"
}
public void ButtonWasPressed()
{
_slot?.Execute(); // БАЦ! Нажали кнопку, конверт улетел выполнять свою работу.
_lastCommand = _slot; // Запомнили, что это мы сейчас натворили.
}
public void UndoButtonWasPressed()
{
_lastCommand?.Undo(); // ОЙ, БЛЯ! Жмём "отмена". Последняя команда откатывается.
_lastCommand = null;
}
}
// 5. Ну и главный по тарелочкам, клиентский код, где всё собирается.
class Program
{
static void Main()
{
// Есть лампочка в гостиной. Получатель.
Light livingRoomLight = new Light("Гостиной");
// Пишем два приказа-конверта. Один на включение, другой на выключение.
ICommand lightOn = new LightOnCommand(livingRoomLight);
ICommand lightOff = new LightOffCommand(livingRoomLight);
// Берем в руки пульт.
SimpleRemoteControl remote = new SimpleRemoteControl();
// Суём в пульт конверт "ВКЛЮЧИТЬ" и жмём кнопку.
remote.SetCommand(lightOn);
remote.ButtonWasPressed(); // И свет загорелся!
// Теперь суём конверт "ВЫКЛЮЧИТЬ" и жмём.
remote.SetCommand(lightOff);
remote.ButtonWasPressed(); // Тьма.
// А теперь фокус с отменой! Снова включаем...
remote.SetCommand(lightOn);
remote.ButtonWasPressed(); // Свет есть.
remote.UndoButtonWasPressed(); // Ой, передумал! *Жмяк* — и свет пропал. Вообще красота!
}
}
Вот и вся магия, ёпта. Главная фишка — развязал руки. Пульту похуй, что там за прибор, он просто тыкает в кнопку. А команда — это умный посредник, который знает КОМУ и ЧТО сказать. Хочешь записать все действия в лог — пожалуйста, команды же объекты. Хочешь сделать очередь задач — да легко, складывай их в список и выполняй по одной. Хочешь макрос «Прийти с работы» — создай команду, которая внутри себя выполняет кучу других команд. Удобно, модульно, и отменять нехуёво.
Прям как в жизни: начальник (клиент) даёт секретарше (пульту) приказ (команду) для отдела (получателя). Секретарше всё равно, что в приказе, её дело — передать. А отдел уже голову ломает, как это выполнить. А если начальник — мудак, он всегда может сказать «Отмена, я передумал».