Паттерн Команда инкапсулирует запрос в виде объекта, делая возможной параметризацию клиентских объектов с другими запросами, организацию очереди или регистрацию запросов, а также поддержку отмены операций.
Паттерн Команда отделяет объект, выдающий запросы, от объекта, который умеет эти запросы выполнять. Объект команды инкапсулирует получателя с операцией или набором операций.
Инициатор вызываем метод Execute() объекта команды что приводит к выполнению соответствующих операций с получателем. Паттерн команды могут поддерживать механизмы отмены, восстанавливающий объект в состоянии до последнего вызова метода Execute();
Макрокоманды— простое расширение паттерна Команда, позволяющее выполнять цепочки из нескольких команд, в них так же легко реализуется механизм отмены.
Команды также могут использоваться для реализации система регистрации команд и поддержки транзакций.
В данном примере рассмотрим две вариации паттерна, первый пример будет без возможности механизма отмены, другой с отменой, сделал так специально что бы было проще разобраться в паттерне.
Пример 1 представим мы разрабатываем систему умный дом, у нас есть ТЗ которая подразумевает управление освещением в доме. Я не стал усложнять пример, отдельно взятой комнате, будем включать и выключать освещение во всем доме)
using System;
namespace Паттерн_Команда
{
/// <summary>
/// Единственный интерфейс с 2 методами
/// </summary>
public interface Command
{
void Execute();
void Undo();
}
public class LightCommand : Command
{
Light light;
public LightCommand(Light light) => this.light = light;
public void Execute() => light.On();
public void Undo() => light.Off();
}
/// <summary>
/// Класс управляет набором объектов команд, при нажамии одной из кнопок активизируем методы Execute Undo
/// Сам класс ничего не знает о тех классах, к которым он обращается, так как он отделен от них объектом команды
/// </summary>
public class SimpleRemoteControl
{
Command slot;
public SimpleRemoteControl() { }
public void SetCommand(Command command) => slot = command;
public void ButtonOn() => slot.Execute();
public void ButtonOff() => slot.Undo();
}
public class Light
{
public Light(){}
public void Off() => Console.WriteLine("Выключили свет");
public void On() => Console.WriteLine("Включили свет");
}
class Program
{
static void Main(string[] args)
{
SimpleRemoteControl remote = new SimpleRemoteControl();
LightCommand lightCommand = new LightCommand(new Light());
remote.SetCommand(lightCommand);
remote.ButtonOn();
remote.ButtonOff();
Console.ReadKey();
}
}
}

Пример 2 основан на механизме отмены, будем так же оборудовать наш умный дом новой функцией управления вентилятором. У нас будет несколько режимов, а так же возможность возврата предыдущего режима.
using System;
using System.Collections.Generic;
namespace Паттерн_Команда
{
///// <summary>
///// Единственный интерфейс с 2 методами
///// </summary>
public interface Command
{
void Execute();
void Undo();
}
public enum Mode
{
High = 3,
Medium = 2,
Low = 1,
Off = 0,
}
public class CeilingFan
{
Mode speed;
string location;
public CeilingFan(string location)
{
this.location = location;
speed = Mode.Off;
}
public void High()
{
speed = Mode.High;
}
public void Medium()
{
speed = Mode.Medium;
}
public void Low()
{
speed = Mode.Low;
}
public void Off()
{
speed = Mode.Off;
}
public Mode GetSpeed()
{
return speed;
}
}
internal class CeilingFanOffCommand : Command
{
private CeilingFan ceilingFan;
Mode prevSpeed;
public CeilingFanOffCommand(CeilingFan ceilingFan)
{
this.ceilingFan = ceilingFan;
}
public void Execute()
{
prevSpeed = ceilingFan.GetSpeed();
ceilingFan.Off();
Console.WriteLine("Режим выключения");
}
public void Undo()
{
switch (prevSpeed)
{
case Mode.High:
ceilingFan.High();
break;
case Mode.Medium:
ceilingFan.Medium();
break;
case Mode.Low:
ceilingFan.Low();
break;
case Mode.Off:
ceilingFan.Off();
break;
}
Console.WriteLine("Возобновили режим выключения");
}
}
internal class CeilingFanMediumCommand : Command
{
private CeilingFan ceilingFan;
Mode prevSpeed;
public CeilingFanMediumCommand(CeilingFan ceilingFan)
{
this.ceilingFan = ceilingFan;
}
public void Execute()
{
prevSpeed = ceilingFan.GetSpeed();
ceilingFan.Medium();
Console.WriteLine("Режим средней мощности");
}
public void Undo()
{
switch (prevSpeed)
{
case Mode.High:
ceilingFan.High();
break;
case Mode.Medium:
ceilingFan.Medium();
break;
case Mode.Low:
ceilingFan.Low();
break;
case Mode.Off:
ceilingFan.Off();
break;
}
Console.WriteLine("Возобновили режим средней мощности");
}
}
public class CeilingFanHighCommand : Command
{
CeilingFan ceilingFan;
Mode prevSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan)
{
this.ceilingFan = ceilingFan;
}
public void Execute()
{
prevSpeed = ceilingFan.GetSpeed();
ceilingFan.High();
}
public void Undo()
{
switch (prevSpeed)
{
case Mode.High:
ceilingFan.High();
break;
case Mode.Medium:
ceilingFan.Medium();
break;
case Mode.Low:
ceilingFan.Low();
break;
case Mode.Off:
ceilingFan.Off();
break;
}
Console.WriteLine("Режим максимальной мощности");
}
}
///// <summary>
///// Класс управляет набором объектов команд, при нажамии одной из кнопок активизируем методы Execute UnExecute
///// Сам класс ничего не знает о тех классах, к которым он обращается, так как он отделен от них объектом команды
///// </summary>
public class SimpleRemoteControl
{
List<Command> onCommands;
List<Command> offCommands;
Command undoCommand;
public SimpleRemoteControl()
{
onCommands = new List<Command>{null,null,null,null,null,null};
offCommands = new List<Command> { null, null, null, null, null, null };
}
public void SetCommand(int slot, Command command, Command offComand)
{
onCommands[slot] = command;
offCommands[slot] = offComand;
}
public void ButtonOn(int slot)
{
onCommands[slot].Execute();
undoCommand = onCommands[slot];
Console.WriteLine("Включили вентелятор");
}
public void ButtonOff(int slot)
{
offCommands[slot].Execute();
undoCommand = offCommands[slot];
Console.WriteLine("Выключили вентелятор");
}
internal void ButtonUndo()
{
undoCommand.Undo();
Console.WriteLine("Возврат предыдущего режима");
}
}
class Program
{
static void Main(string[] args)
{
SimpleRemoteControl remoteControl = new SimpleRemoteControl();
CeilingFan ceilingFan = new CeilingFan("Спальня");
CeilingFanMediumCommand ceilingFanMedium = new CeilingFanMediumCommand(ceilingFan);
CeilingFanHighCommand ceilingFanHigh = new CeilingFanHighCommand(ceilingFan); //создаем экземпляры 3 команды высокой, средней и для выключения
CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan);
remoteControl.SetCommand(0, ceilingFanMedium, ceilingFanOff);
remoteControl.SetCommand(1, ceilingFanHigh, ceilingFanOff);
remoteControl.ButtonOn(0);
remoteControl.ButtonOff(0);
remoteControl.ButtonUndo();
remoteControl.ButtonOn(1);
remoteControl.ButtonUndo();
Console.ReadKey();
}
}
}

