Команда (Command)
Другое имя
Action (действие), Transaction (транзакция)
Назначение
Упаковка действий (запросов) в объекты, позволяя ставить запросы в очередь, выполнять логирование, комбинирование действий, поддерживать отмену операций.
Описание
Паттерн Command обеспечивает разделение между тем, когда нужно выполнить операцию и тем, как ее нужно выполнить. Созданный объект класса Command передается объекту, который должен выполнить эту команду (объект класса Invoker). При создании объекту класса Command передается объект класса Receiver, содержащего ряд методов для выполнения операции.
Реализация
Диаграмма классов
Участники
- Command - команда
Объявляет интерфейс для выполнения операции, состоящий из метода Execute и, возможно, UnExecute
- ConcreteCommand - конкретная команда
Реализует метод Execute, вызывая методы Receiverа
- Client - клиент
Создает объект класса ConcreteCommand и устанавливает его получателя (Receiverа). Получатель команды - это дурацкое непонятное название, смысл которого в следующем. Receiver называется получателем команды если команда вызывает его методы.
- Invoker - инициатор
Обращается к команде для выполнения запроса (вызывает ее метод Execute)
- Receiver - получатель
Умеет выполнять операции, необходимые для реализации команд. Как правило, передается в команду в качестве параметра конструктора.
Пример
using System;
using System.Collections.Generic;
class AruthmeticUnit // Receiver
{
private double res = 0;
public void Add(double x)
{
res += x;
}
public void Sub(double x)
{
res -= x;
}
public void Div(double x)
{
res /= x;
}
public void Mult(double x)
{
res *= x;
}
public double Result
{
get { return res; }
}
}
abstract class Command
{
public readonly AruthmeticUnit unit;
public Command(AruthmeticUnit unit)
{
this.unit = unit;
}
public abstract void Execute();
}
class BinaryCommand: Command
{
private char op;
private double x;
public BinaryCommand(AruthmeticUnit unit, char op, double x): base(unit)
{
this.op = op;
this.x = x;
}
public override void Execute()
{
switch (op)
{
case '+':
unit.Add(x);
break;
case '-':
unit.Sub(x);
break;
case '*':
unit.Mult(x);
break;
case '/':
unit.Div(x);
break;
}
}
}
class ChangeSignCommand: Command
{
public ChangeSignCommand(AruthmeticUnit unit): base(unit)
{ }
public override void Execute()
{
unit.Mult(-1);
}
}
class My // Одновременно Client (создает команды и Receiverа) и Invoker (выполняет команды)
{
public static void Main()
{
// Действия Clientа
var unit = new AruthmeticUnit();
var l = new List<Command>();
l.Add(new BinaryCommand(unit,'+',2));
l.Add(new BinaryCommand(unit,'*',3));
l.Add(new BinaryCommand(unit,'-',1));
l.Add(new BinaryCommand(unit,'*',2));
l.Add(new ChangeSignCommand(unit));
// Действия Invokerа
foreach (var c in l)
c.Execute();
Console.WriteLine(unit.Result);
}
}
Нетрудно реализовать метод UnExecute, выподняющий противоположное действие. Для отката действий необходимо выполнить UnExecute для команд в обратном порядке.
Достоинства и недостатки
- Отделение вызова операции от алгоритма выполнения этой операции
- Совместное использование команд разными объектами
- Команда как объект обладает всеми преимуществами объектов: ее можно расширять, можно скрывать данные и проч.
- Из простых команд можно собирать составные (макрокоманды - суперпозиция команд)