- Команда (шаблон проектирования)
-
Шаблон проектирования Команда Command Тип: поведенческий
Назначение: для обработки команды в виде объекта
Родственные шаблоны: Описан в Design Patterns Да
Команда — шаблон проектирования, используемый при объектно-ориентированном программировании, представляющий действие. Объект команды заключает в себе само действие и его параметры.
Содержание
Цель
Создание структуры, в которой класс-отправитель и класс-получатель не зависят друг от друга напрямую. Организация обратного вызова к классу, который включает в себя класс-отправитель.
Описание
Паттерн поведения объектов,известен так же под именем Action(действие).
Обеспечивает обработку команды в виде объекта, что позволяет сохранять её, передавать в качестве параметра методам, а также возвращать её в виде результата, как и любой другой объект.
Например, библиотека печати может иметь класс PrintJob. Для его использования можно создать объект PrintJob, установить необходимые параметры, и вызвать метод, непосредственно отсылающий задание на печать.
Примеры
Пример на С++
Исходный текст на языке C++class Document { vector<string> data; public: 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 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 Reciver { 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(); } } void Show() { doc.Show(); } }; int main() { char s = '1'; int line, line_b; string str; Reciver 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; } return 0; }
Пример на С#
Исходный текст на языке C#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 - 1) _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(); // Добавляем операцию к списку отмены _commands.Add(command); _current++; } } }
Пример на Java
Исходный текст на языке Java/*the Invoker class*/ public class Switch { private Command flipUpCommand; private Command flipDownCommand; public Switch(Command flipUpCmd,Command flipDownCmd){ this.flipUpCommand=flipUpCmd; this.flipDownCommand=flipDownCmd; } 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(); } }
Пример JavaScript
Исходный текст на языке JavaScript// 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"
Пример на PHP5
Исходный текст на языке PHP5<?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) { // Создаем команду операции и выполняем её $this->command = new CalculatorCommand($this->calculator, $operator, $operand); $this->command->Execute(); // Добавляем операцию к массиву операций и увеличиваем счетчик текущей операции $this->commands[]=$this->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); ?>
Пример на VB.NET
Исходный текст на языке VB.NETImports 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
Ссылки
- Паттерн проектирования Command (Команда) — назначение, описание, реализация на С++, достоинства и недостатки
Шаблоны проектирования Основные Порождающие Структурные Поведенческие Интерпретатор • Итератор • Команда • Наблюдатель • Посетитель • Посредник • Состояние • Стратегия • Хранитель • Цепочка обязанностей • Шаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнение • Планировщик Для улучшения этой статьи желательно?: - Найти и оформить в виде сносок ссылки на авторитетные источники, подтверждающие написанное.
Категории:- Шаблоны проектирования
- Объектно-ориентированное программирование
Wikimedia Foundation. 2010.