Команда (Command) — различия между версиями
Admin (обсуждение | вклад) (→Диаграмма классов) |
Admin (обсуждение | вклад) (→Варианты) |
||
(не показано 13 промежуточных версий этого же участника) | |||
Строка 2: | Строка 2: | ||
__NOTOC__ | __NOTOC__ | ||
=== Другое имя === | === Другое имя === | ||
− | Action (действие), Transaction (транзакция) | + | '''Action''' (действие), '''Transaction''' (транзакция) |
+ | |||
=== Назначение === | === Назначение === | ||
− | Упаковка действий в объекты | + | Упаковка действий (запросов) в объекты, позволяя ставить запросы в очередь, выполнять логирование, комбинирование действий, поддерживать отмену операций. |
=== Описание === | === Описание === | ||
+ | Паттерн Command обеспечивает разделение между тем, когда нужно выполнить операцию и тем, как ее нужно выполнить. | ||
+ | Созданный объект класса Command передается объекту, который должен выполнить эту команду (объект класса Invoker). | ||
+ | При создании объекту класса Command передается объект класса Receiver, содержащего ряд методов для выполнения операции. | ||
=== Реализация === | === Реализация === | ||
Строка 13: | Строка 17: | ||
==== Участники==== | ==== Участники==== | ||
− | * | + | * '''Command''' - команда |
+ | Объявляет интерфейс для выполнения операции, состоящий из метода Execute и, возможно, UnExecute | ||
+ | * '''ConcreteCommand''' - конкретная команда | ||
+ | Реализует метод Execute, вызывая методы Receiverа | ||
+ | * '''Client''' - клиент | ||
+ | Создает объект класса ConcreteCommand и устанавливает его получателя (Receiverа). Получатель команды - это дурацкое непонятное название, смысл которого в следующем. | ||
+ | Receiver называется получателем команды если команда вызывает его методы. | ||
+ | * '''Invoker''' - инициатор | ||
+ | Обращается к команде для выполнения запроса (вызывает ее метод Execute) | ||
+ | * '''Receiver''' - получатель | ||
+ | Умеет выполнять операции, необходимые для реализации команд. Как правило, передается в команду в качестве параметра конструктора. | ||
=== Пример === | === Пример === | ||
+ | [http://pascalabc.net/WDE/?file=PatternsCS/CalcCommand.cs Этот пример в WDE] | ||
<source lang="Csharp"> | <source lang="Csharp"> | ||
+ | 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); | ||
+ | } | ||
+ | } | ||
</source> | </source> | ||
+ | |||
+ | Нетрудно реализовать метод UnExecute, выподняющий противоположное действие. Для отката действий необходимо выполнить UnExecute для команд в обратном порядке. | ||
=== Достоинства и недостатки === | === Достоинства и недостатки === | ||
− | * | + | * Отделение вызова операции от алгоритма выполнения этой операции |
+ | * Совместное использование команд разными объектами | ||
+ | * Команда как объект обладает всеми преимуществами объектов: ее можно расширять, можно скрывать данные и проч. | ||
+ | * Из простых команд можно собирать составные (макрокоманды - суперпозиция команд) | ||
=== Варианты === | === Варианты === | ||
− | * | + | * Команда, не зависящая от получателя Receiver и реализующая действие самостоятельно. |
+ | * Поддержка в команде информации, необходимой для операции UnExecute. Получатель должен предоставлять команде достаточно информации, позволяя вернуться ей в исходное состояние. Команду, допускающую отмену, придется скопировать в список истории | ||
+ | * При выполнении и отмене команды могут накапливаться ошибки. В этих случаях, возможно, в команде необходимо сохранять снимок части данных до выполнения команды. | ||
+ | * Можно использовать обобщенные классы, где в качестве параметра обобщения выступает класс получателя. Сделать это можно только если команда не имеет параметров и не требует отката. | ||
+ | * Если команда не требует отката, в метод Execute можно передавать часть или все параметры команды. |
Текущая версия на 09:07, 2 августа 2014
Другое имя
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 для команд в обратном порядке.
Достоинства и недостатки
- Отделение вызова операции от алгоритма выполнения этой операции
- Совместное использование команд разными объектами
- Команда как объект обладает всеми преимуществами объектов: ее можно расширять, можно скрывать данные и проч.
- Из простых команд можно собирать составные (макрокоманды - суперпозиция команд)
Варианты
- Команда, не зависящая от получателя Receiver и реализующая действие самостоятельно.
- Поддержка в команде информации, необходимой для операции UnExecute. Получатель должен предоставлять команде достаточно информации, позволяя вернуться ей в исходное состояние. Команду, допускающую отмену, придется скопировать в список истории
- При выполнении и отмене команды могут накапливаться ошибки. В этих случаях, возможно, в команде необходимо сохранять снимок части данных до выполнения команды.
- Можно использовать обобщенные классы, где в качестве параметра обобщения выступает класс получателя. Сделать это можно только если команда не имеет параметров и не требует отката.
- Если команда не требует отката, в метод Execute можно передавать часть или все параметры команды.