Команда | |
---|---|
Command | |
![]() | |
Тип | поведенческий |
Назначение | для обработки команды в виде объекта |
Родственные шаблоны | Компоновщик, Хранитель, Прототип, Одиночка |
Описан в Design Patterns | Да |
Команда (англ. Command) — поведенческий шаблон проектирования, используемый при объектно-ориентированном программировании, представляющий действие. Объект команды заключает в себе само действие и его параметры.
Создание структуры, в которой класс-отправитель и класс-получатель не зависят друг от друга напрямую. Организация обратного вызова к классу, который включает в себя класс-отправитель.
В объектно-ориентированном программировании шаблон проектирования Команда является поведенческим шаблоном, в котором объект используется для инкапсуляции всей информации, необходимой для выполнения действия или вызова события в более позднее время. Эта информация включает в себя имя метода, объект, который является владельцем метода и значения параметров метода.
Четыре термина всегда связаны с шаблоном Команда: команды (command), приёмник команд (receiver), вызывающий команды (invoker) и клиент (client). Объект Command знает о приёмнике и вызывает метод приемника. Значения параметров приёмника сохраняются в команде. Вызывающий объект (invoker) знает, как выполнить команду и, возможно, делает учёт и запись выполненных команд. Вызывающий объект (invoker) ничего не знает о конкретной команде, он знает только об интерфейсе. Оба объекта (вызывающий объект и несколько объектов команд) принадлежат объекту клиента (client). Клиент решает, какие команды выполнить и когда. Чтобы выполнить команду он передает объект команды вызывающему объекту (invoker).
Использование командных объектов упрощает построение общих компонентов, которые необходимо делегировать или выполнять вызовы методов в любое время без необходимости знать методы класса или параметров метода. Использование вызывающего объекта (invoker) позволяет ввести учёт выполненных команд без необходимости знать клиенту об этой модели учёта (такой учёт может пригодиться, например, для реализации отмены и повтора команд).
Шаблон Команда может быть полезен в следующих случаях.
В Swing и Borland Delphi Action (действие) является объектом команды. В дополнение к способности выполнить нужную команду, Action может иметь связанную с ним иконку, сочетание клавиш, текст всплывающей подсказки и так далее. Кнопка на панели инструментов или пункт меню могут быть полностью инициализированы с использованием только объекта Action.
Если все действия пользователя представлены в виде объектов команды, программа может записать последовательность действий, просто сохраняя список командных объектов в том порядке, в котором они выполняются. Затем она может «воспроизвести» одни и те же действия, выполняя те же объекты команд в той же последовательности.
Если все действия пользователя в программе реализованы в виде командных объектов, программа может сохранить стек последних выполненных команд. Когда пользователь хочет отменить команду, программа просто выталкивает последний объект команды и выполняет его метод undo().
Можно отправить объекты команд по сети для выполнения на другой машине, например действие игрока в компьютерной игре.
Предположим, что программа имеет последовательность команд, которые она выполняет по порядку. Если каждый объект команды имеет метод getEstimatedDuration() (получить оценочную длительность), программа может легко оценить общую продолжительность процесса. Она может показать индикатор выполнения, который отражает, насколько близка программа к завершению всех задач.
Типичный класс пула потоков общего назначения может иметь метод addTask(), который добавляет рабочий элемент к внутренней очереди заданий ожидающих своего выполнения. Он поддерживает пул потоков, которые выполняют команды из очереди. Элементы в очереди являются объектами команд. Как правило, эти объекты реализуют общий интерфейс, такой как java.lang.Runnable, что позволяет пулу потоков запустить команды на выполнение, даже если он сам был написан без каких-либо знаний о конкретных задачах, для которых он будет использоваться.
Аналогично операции «отмена» система управления базами данных (СУБД) или установщик программного обеспечения может хранить список операций, которые были или будут выполнены. Если одна из них закончится неудачей, то все остальные могут быть отменены или быть отброшены (обычно называется откат). Например, если две связанные между собой таблицы базы данных должны быть обновлены, а второе обновление терпит неудачу, то система может откатить транзакцию, чтобы первая таблица не содержала недопустимую ссылку.
Часто мастер (мастер установки или любой другой) представляет несколько страниц конфигурации для одного действия, которое происходит только тогда, когда пользователь нажимает на кнопку «Готово» на последней странице. В этих случаях, естественный способ отделить код пользовательского интерфейса от кода приложения является реализация мастера с помощью объекта команд. Объект команда создается при первом отображении мастера. Каждая страница мастера сохраняет свои изменения в объекте команды, поэтому объект заполняется по мере перехода пользователя. Кнопка «Готово» просто запускает метод execute() на выполнение.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Document
{
vector<string> data;
public:
Document()
{
data.reserve(100); // at least for 100 lines
}
void Insert( int line, const string & str )
{
if ( line <= data.size() )
data.insert( data.begin() + line, str );
else
cout << "Error!" << endl;
}
void Remove( int line )
{
if( !( line>data.size() ) )
data.erase( data.begin() + line );
else
cout << "Error!" << endl;
}
string & operator [] ( int x )
{
return data[x];
}
void Show()
{
for( int i = 0; i<data.size(); ++i )
{
cout << i + 1 << ". " << data[i] << endl;
}
}
};
class Command
{
protected:
Document * doc;
public:
virtual ~Command() {}
virtual void Execute() = 0;
virtual void unExecute() = 0;
void setDocument( Document * _doc )
{
doc = _doc;
}
};
class InsertCommand : public Command
{
int line;
string str;
public:
InsertCommand( int _line, const string & _str ): line( _line ), str( _str ) {}
void Execute()
{
doc->Insert( line, str );
}
void unExecute()
{
doc->Remove( line );
}
};
class Receiver
{
vector<Command*> DoneCommands;
Document doc;
Command* command;
public:
void Insert( int line, string str )
{
command = new InsertCommand( line, str );
command->setDocument( &doc );
command->Execute();
DoneCommands.push_back( command );
}
void Undo()
{
if( DoneCommands.size() == 0 )
{
cout << "There is nothing to undo!" << endl;
}
else
{
command = DoneCommands.back();
DoneCommands.pop_back();
command->unExecute();
// Don't forget to delete command!!!
delete command;
}
}
void Show()
{
doc.Show();
}
};
int main()
{
char s = '1';
int line, line_b;
string str;
Receiver res;
while( s!= 'e' )
{
cout << "What to do: \n1.Add a line\n2.Undo last command" << endl;
cin >> s;
switch( s )
{
case '1':
cout << "What line to insert: ";
cin >> line;
--line;
cout << "What to insert: ";
cin >> str;
res.Insert( line, str );
break;
case '2':
res.Undo();
break;
}
cout << "$$$DOCUMENT$$$" << endl;
res.Show();
cout << "$$$DOCUMENT$$$" << endl;
}
}
using System;
using System.Collections.Generic;
namespace Command
{
class MainApp
{
static void Main()
{
// Создаем пользователя.
User user = new User();
// Пусть он что-нибудь сделает.
user.Compute('+', 100);
user.Compute('-', 50);
user.Compute('*', 10);
user.Compute('/', 2);
// Отменяем 4 команды
user.Undo(4);
// Вернём 3 отменённые команды.
user.Redo(3);
// Ждем ввода пользователя и завершаемся.
Console.Read();
}
}
// "Command" : абстрактная Команда
abstract class Command
{
public abstract void Execute();
public abstract void UnExecute();
}
// "ConcreteCommand" : конкретная команда
class CalculatorCommand : Command
{
char @operator;
int operand;
Calculator calculator;
// Constructor
public CalculatorCommand(Calculator calculator,
char @operator, int operand)
{
this.calculator = calculator;
this.@operator = @operator;
this.operand = operand;
}
public char Operator
{
set{ @operator = value; }
}
public int Operand
{
set{ operand = value; }
}
public override void Execute()
{
calculator.Operation(@operator, operand);
}
public override void UnExecute()
{
calculator.Operation(Undo(@operator), operand);
}
// Private helper function : приватные вспомогательные функции
private char Undo(char @operator)
{
char undo;
switch(@operator)
{
case '+': undo = '-'; break;
case '-': undo = '+'; break;
case '*': undo = '/'; break;
case '/': undo = '*'; break;
default : undo = ' '; break;
}
return undo;
}
}
// "Receiver" : получатель
class Calculator
{
private int curr = 0;
public void Operation(char @operator, int operand)
{
switch(@operator)
{
case '+': curr += operand; break;
case '-': curr -= operand; break;
case '*': curr *= operand; break;
case '/': curr /= operand; break;
}
Console.WriteLine(
"Current value = {0,3} (following {1} {2})",
curr, @operator, operand);
}
}
// "Invoker" : вызывающий
class User
{
// Initializers
private Calculator _calculator = new Calculator();
private List<Command> _commands = new List<Command>();
private int _current = 0;
public void Redo(int levels)
{
Console.WriteLine("\n---- Redo {0} levels ", levels);
// Делаем возврат операций
for (int i = 0; i < levels; i++)
if (_current < _commands.Count)
_commands[_current++].Execute();
}
public void Undo(int levels)
{
Console.WriteLine("\n---- Undo {0} levels ", levels);
// Делаем отмену операций
for (int i = 0; i < levels; i++)
if (_current > 0)
_commands[--_current].UnExecute();
}
public void Compute(char @operator, int operand)
{
// Создаем команду операции и выполняем её
Command command = new CalculatorCommand(
_calculator, @operator, operand);
command.Execute();
if (_current < _commands.Count)
{
// если "внутри undo" мы запускаем новую операцию,
// надо обрубать список команд, следующих после текущей,
// иначе undo/redo будут некорректны
_commands.RemoveRange(_current, _commands.Count - _current);
}
// Добавляем операцию к списку отмены
_commands.Add(command);
_current++;
}
}
}
/*the Invoker class*/
public class Switch {
private Command flipUpCommand;
private Command flipDownCommand;
public Switch(Command flipUpCommand,Command flipDownCommand){
this.flipUpCommand=flipUpCommand;
this.flipDownCommand=flipDownCommand;
}
public void flipUp(){
flipUpCommand.execute();
}
public void flipDown(){
flipDownCommand.execute();
}
}
/*Receiver class*/
public class Light{
public Light(){ }
public void turnOn(){
System.out.println("The light is on");
}
public void turnOff(){
System.out.println("The light is off");
}
}
/*the Command interface*/
public interface Command{
void execute();
}
/*the Command for turning on the light*/
public class TurnOnLightCommand implements Command{
private Light theLight;
public TurnOnLightCommand(Light light){
this.theLight=light;
}
public void execute(){
theLight.turnOn();
}
}
/*the Command for turning off the light*/
public class TurnOffLightCommand implements Command{
private Light theLight;
public TurnOffLightCommand(Light light){
this.theLight=light;
}
public void execute(){
theLight.turnOff();
}
}
/*The test class*/
public class TestCommand{
public static void main(String[] args){
Light l=new Light();
Command switchUp=new TurnOnLightCommand(l);
Command switchDown=new TurnOffLightCommand(l);
Switch s=new Switch(switchUp,switchDown);
s.flipUp();
s.flipDown();
}
}
from abc import ABCMeta, abstractmethod
class Troop:
"""
Receiver - объект военного отряда
"""
def move(self, direction: str) -> None:
"""
Начать движение в определенном направлении
"""
print('Отряд начал движение {}'.format(direction))
def stop(self) -> None:
"""
Остановиться
"""
print('Отряд остановился')
class Command(metaclass=ABCMeta):
"""
Базовый класс для всех команд
"""
@abstractmethod
def execute(self) -> None:
"""
Приступить к выполнению команды
"""
pass
@abstractmethod
def unexecute(self) -> None:
"""
Отменить выполнение команды
"""
pass
class AttackCommand(Command):
"""
Команда для выплнения атаки
"""
def __init__(self, troop: Troop) -> None:
"""
Constructor.
:param troop: отряд, с которым ассоциируется команда
"""
self.troop = troop
def execute(self) -> None:
self.troop.move('вперед')
def unexecute(self) -> None:
self.troop.stop()
class RetreatCommand(Command):
"""
Команда для выполнения отступления
"""
def __init__(self, troop: Troop) -> None:
"""
Constructor.
:param troop: отряд, с которым ассоциируется команда
"""
self.troop = troop
def execute(self) -> None:
self.troop.move('назад')
def unexecute(self) -> None:
self.troop.stop()
class TroopInterface:
"""
Invoker - интерфейс, через который можно отдать команды определенному отряду
"""
def __init__(self, attack: AttackCommand, retreat: RetreatCommand) -> None:
"""
Constructor.
:param attack: команда для выполнения атаки
:param retreat: команда для выполнения отступления
"""
self.attack_command = attack
self.retreat_command = retreat
self.current_command = None # команда, выполняющаяся в данный момент
def attack(self) -> None:
self.current_command = self.attack_command
self.attack_command.execute()
def retreat(self) -> None:
self.current_command = self.retreat_command
self.retreat_command.execute()
def stop(self) -> None:
if self.current_command:
self.current_command.unexecute()
self.current_command = None
else:
print('Отряд не может остановиться, так как не двигается')
if __name__ == '__main__':
troop = Troop()
interface = TroopInterface(AttackCommand(troop), RetreatCommand(troop))
interface.attack()
interface.stop()
interface.retreat()
interface.stop()
// Command: абстрактная Команда
function Command() {
this.execute = function() {};
this.unExecute = function() {};
}
// ConcreteCommand: конкретная команда
function CalculatorCommand() {
var calculator;
var operator;
var operand;
this.execute = function(newCalculator, newOperator, newOperand) {
// установка параметров команды
if (typeof(newCalculator)=="object" && typeof(newOperator)=="string" && typeof(newOperand)=="number") {
calculator = newCalculator;
operator = newOperator;
operand = newOperand;
}
// исполнение команды
calculator.operation(operator, operand);
};
this.unExecute = function() {
// исполнение обратной команды
calculator.operation(undo(operator), operand);
};
function undo(operator) {
// функция вернёт оператор, обратный переданному
// при желании, можно воспользоваться замыканием и не передавать оператор
switch(operator) {
case '+': return '-'; break;
case '-': return '+'; break;
}
return ' '; // результат по умолчанию
}
}
CalculatorCommand.prototype = new Command();
CalculatorCommand.prototype.constructor = CalculatorCommand;
// Receiver: получатель
function Calculator() {
var val = 0;
this.operation = function(operator, operand) {
// производим операцию
switch(operator) {
case '+':
val += operand;
debug(operator, operand);
break;
case '-':
val -= operand;
debug(operator, operand);
break;
default:
alert("Неизвестный оператор");
break;
}
};
function debug(operator, operand) {
alert("Текущее значение: "+ val +"\nОперация: "+ operator + operand);
}
}
// Invoker: вызывающий
function User() {
var calculator = new Calculator();
var commands = []; // массив команд
current = 0; // номер текущей команды
this.compute = function(operator, operand) {
var newCommand = new CalculatorCommand();
if (current<commands.length-1) {
// если "внутри undo" мы запускаем новую операцию,
// надо обрубать список команд, следующих после текущей,
// иначе undo/redo будут некорректны
commands.splice(current);
}
newCommand.execute(calculator, operator, operand);
commands.push(newCommand);
current++;
};
this.undo = function(levels) {
alert("отмена ("+ levels +")");
for (i=0; i<levels; i++) {
if (current > 0) {
commands[--current].unExecute();
}
}
};
this.redo = function(levels) {
alert("возврат ("+ levels +")");
for (i=0; i<levels; i++) {
if (current < commands.length) {
commands[current++].execute();
}
}
};
}
// использование
var u = new User();
u.compute("+", 2); // 2, "+2"
u.compute("+", 3); // 5, "+3"
u.compute("-", 1); // 4, "-1"
u.compute("+", 6); // 10, "+6"
u.undo(3); // 4, "-6"
// 5, "+1"
// 2, "-3"
u.redo(2); // 5, "+3"
// 4, "-1"
u.undo(2); // 5, "+1"
// 2, "-3"
u.compute("+", 8); // 10, "+8"
u.undo(1); // 2, "-8"
u.redo(2); // 10, "+8"
// превышение длинны commands
u.compute("+", 9); // 19, "+9"
<?php
/**
* Абстрактый класс "команды"
* @abstract
*/
abstract class Command
{
public abstract function Execute();
public abstract function UnExecute();
}
/**
* Класс конкретной "команды"
*/
class CalculatorCommand extends Command
{
/**
* Текущая операция команды
*
* @var string
*/
public $operator;
/**
* Текущий операнд
*
* @var mixed
*/
public $operand;
/**
* Класс, для которого предназначена команда
*
* @var object of class Calculator
*/
public $calculator;
/**
* Конструктор
*
* @param object $calculator
* @param string $operator
* @param mixed $operand
*/
public function __construct($calculator, $operator, $operand)
{
$this->calculator = $calculator;
$this->operator = $operator;
$this->operand = $operand;
}
/**
* Переопределенная функция parent::Execute()
*/
public function Execute()
{
$this->calculator->Operation($this->operator, $this->operand);
}
/**
* Переопределенная функция parent::UnExecute()
*/
public function UnExecute()
{
$this->calculator->Operation($this->Undo($this->operator), $this->operand);
}
/**
* Какое действие нужно отменить?
*
* @private
* @param string $operator
* @return string
*/
private function Undo($operator)
{
//каждому произведенному действию найти обратное
switch($operator)
{
case '+': $undo = '-'; break;
case '-': $undo = '+'; break;
case '*': $undo = '/'; break;
case '/': $undo = '*'; break;
default : $undo = ' '; break;
}
return $undo;
}
}
/**
* Класс получатель и исполнитель "команд"
*/
class Calculator
{
/**
* Текущий результат выполнения команд
*
* @private
* @var int
*/
private $curr = 0;
public function Operation($operator,$operand)
{
//выбрать оператора для вычисления результата
switch($operator)
{
case '+': $this->curr+=$operand; break;
case '-': $this->curr-=$operand; break;
case '*': $this->curr*=$operand; break;
case '/': $this->curr/=$operand; break;
}
print("Текущий результат = $this->curr (после выполнения $operator c $operand)");
}
}
/**
* Класс, вызывающий команды
*/
class User
{
/**
* Этот класс будет получать команды на исполнение
*
* @private
* @var object of class Calculator
*/
private $calculator;
/**
* Массив операций
*
* @private
* @var array
*/
private $commands = array();
/**
* Текущая команда в массиве операций
*
* @private
* @var int
*/
private $current = 0;
public function __construct()
{
//создать экземпляр класса, который будет исполнять команды
$this->calculator = new Calculator();
}
/**
* Функция возврата отмененных команд
*
* @param int $levels количество возвращаемых операций
*/
public function Redo($levels)
{
print("\n---- Повторить $levels операций ");
// Делаем возврат операций
for ($i = 0; $i < $levels; $i++)
if ($this->current < count($this->commands) - 1)
$this->commands[$this->current++]->Execute();
}
/**
* Функция отмены команд
*
* @param int $levels количество отменяемых операций
*/
public function Undo($levels)
{
print("\n---- Отменить $levels операций ");
// Делаем отмену операций
for ($i = 0; $i < $levels; $i++)
if ($this->current > 0)
$this->commands[--$this->current]->UnExecute();
}
/**
* Функция выполнения команд
*
* @param string $operator
* @param mixed $operand
*/
public function Compute($operator, $operand)
{
// Создаем команду операции и выполняем её
$command = new CalculatorCommand($this->calculator, $operator, $operand);
$command->Execute();
// Добавляем операцию к массиву операций и увеличиваем счетчик текущей операции
$this->commands[]=$command;
$this->current++;
}
}
$user = new User();
// Произвольные команды
$user->Compute('+', 100);
$user->Compute('-', 50);
$user->Compute('*', 10);
$user->Compute('/', 2);
// Отменяем 4 команды
$user->Undo(4);
// Вернём 3 отменённые команды.
$user->Redo(3);
Imports System.Collections.Generic
Namespace Command
Class Program
Shared Sub Main()
' Создаем пользователя.
Dim user As New User()
' Пусть он что-нибудь сделает.
user.Compute("+"c, 100)
user.Compute("-"c, 50)
user.Compute("*"c, 10)
user.Compute("/"c, 2)
' Отменяем 4 команды
user.Undo(4)
' Вернём 3 отменённые команды.
user.Redo(3)
' Ждем ввода пользователя и завершаемся.
Console.Read()
End Sub
End Class
' "Command" : абстрактная Команда
MustInherit Class Command
Public MustOverride Sub Execute()
Public MustOverride Sub UnExecute()
End Class
' "ConcreteCommand" : конкретная команда
Class CalculatorCommand
Inherits Command
Private m_operator As Char
Private m_operand As Integer
Private calculator As Calculator
' Constructor
Public Sub New(ByVal calculator As Calculator, ByVal [operator] As Char, ByVal operand As Integer)
Me.calculator = calculator
Me.m_operator = [operator]
Me.m_operand = operand
End Sub
Public WriteOnly Property [Operator]() As Char
Set(ByVal value As Char)
m_operator = value
End Set
End Property
Public WriteOnly Property Operand() As Integer
Set(ByVal value As Integer)
m_operand = value
End Set
End Property
Public Overrides Sub Execute()
calculator.Operation(m_operator, m_operand)
End Sub
Public Overrides Sub UnExecute()
calculator.Operation(Undo(m_operator), m_operand)
End Sub
' Private helper function : приватные вспомогательные функции
Private Function Undo(ByVal [operator] As Char) As Char
Dim undo__1 As Char
Select Case [operator]
Case "+"c
undo__1 = "-"c
Exit Select
Case "-"c
undo__1 = "+"c
Exit Select
Case "*"c
undo__1 = "/"c
Exit Select
Case "/"c
undo__1 = "*"c
Exit Select
Case Else
undo__1 = " "c
Exit Select
End Select
Return undo__1
End Function
End Class
' "Receiver" : получатель
Class Calculator
Private curr As Integer = 0
Public Sub Operation(ByVal [operator] As Char, ByVal operand As Integer)
Select Case [operator]
Case "+"c
curr += operand
Exit Select
Case "-"c
curr -= operand
Exit Select
Case "*"c
curr *= operand
Exit Select
Case "/"c
curr /= operand
Exit Select
End Select
Console.WriteLine("Current value = {0,3} (following {1} {2})", curr, [operator], operand)
End Sub
End Class
' "Invoker" : вызывающий
Class User
' Initializers
Private calculator As New Calculator()
Private commands As New List(Of Command)()
Private current As Integer = 0
Public Sub Redo(ByVal levels As Integer)
Console.WriteLine(vbLf & "---- Redo {0} levels ", levels)
' Делаем возврат операций
For i As Integer = 0 To levels - 1
If current < commands.Count - 1 Then
commands(System.Math.Max(System.Threading.Interlocked.Increment(current), current - 1)).Execute()
End If
Next
End Sub
Public Sub Undo(ByVal levels As Integer)
Console.WriteLine(vbLf & "---- Undo {0} levels ", levels)
' Делаем отмену операций
For i As Integer = 0 To levels - 1
If current > 0 Then
commands(System.Threading.Interlocked.Decrement(current)).UnExecute()
End If
Next
End Sub
Public Sub Compute(ByVal [operator] As Char, ByVal operand As Integer)
' Создаем команду операции и выполняем её
Dim command As Command = New CalculatorCommand(calculator, [operator], operand)
command.Execute()
' Добавляем операцию к списку отмены
commands.Add(command)
current += 1
End Sub
End Class
End Namespace
Для улучшения этой статьи желательно: |
Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".
Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.
Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .