Команда (Command)

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск

К основной странице курса

Другое имя

Action (действие), Transaction (транзакция)

Назначение

Упаковка действий (запросов) в объекты, позволяя ставить запросы в очередь, выполнять логирование, комбинирование действий, поддерживать отмену операций.

Описание

Паттерн Command обеспечивает разделение между тем, когда нужно выполнить операцию и тем, как ее нужно выполнить. Созданный объект класса Command передается объекту, который должен выполнить эту команду (объект класса Invoker). При создании объекту класса Command передается объект класса Receiver, содержащего ряд методов для выполнения операции.

Реализация

Диаграмма классов

CommandCommon.png

Участники

  • Command - команда

Объявляет интерфейс для выполнения операции, состоящий из метода Execute и, возможно, UnExecute

  • ConcreteCommand - конкретная команда

Реализует метод Execute, вызывая методы Receiverа

  • Client - клиент

Создает объект класса ConcreteCommand и устанавливает его получателя (Receiverа). Получатель команды - это дурацкое непонятное название, смысл которого в следующем. Receiver называется получателем команды если команда вызывает его методы.

  • Invoker - инициатор

Обращается к команде для выполнения запроса (вызывает ее метод Execute)

  • Receiver - получатель

Умеет выполнять операции, необходимые для реализации команд. Как правило, передается в команду в качестве параметра конструктора.

Пример

Этот пример в WDE

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 для команд в обратном порядке.

Достоинства и недостатки

  • Отделение вызова операции от алгоритма выполнения этой операции
  • Совместное использование команд разными объектами
  • Команда как объект обладает всеми преимуществами объектов: ее можно расширять, можно скрывать данные и проч.
  • Из простых команд можно собирать составные (макрокоманды - суперпозиция команд)

Варианты

  • Команда, не зависящая от получателя Receiver и реализующая действие самостоятельно.
  • Поддержка в команде информации, необходимой для операции UnExecute. Получатель должен предоставлять команде достаточно информации, позволяя вернуться ей в исходное состояние. Команду, допускающую отмену, придется скопировать в список истории
  • При выполнении и отмене команды могут накапливаться ошибки. В этих случаях, возможно, в команде необходимо сохранять снимок части данных до выполнения команды.
  • Можно использовать обобщенные классы, где в качестве параметра обобщения выступает класс получателя. Сделать это можно только если команда не имеет параметров и не требует отката.
  • Если команда не требует отката, в метод Execute можно передавать часть или все параметры команды.