Команда (шаблон проектирования) | это... Что такое Команда (шаблон проектирования)? (original) (raw)

У этого термина существуют и другие значения, см. Команда.

Шаблон проектирования

Команда
Command
Command.gif
Тип: поведенческий
Назначение: для обработки команды в виде объекта
Родственные шаблоны: Компоновщик, Хранитель, Прототип, Одиночка
Описан в Design Patterns Да

Командашаблон проектирования, используемый при объектно-ориентированном программировании, представляющий действие. Объект команды заключает в себе само действие и его параметры.

Содержание

Цель

Создание структуры, в которой класс-отправитель и класс-получатель не зависят друг от друга напрямую. Организация обратного вызова к классу, который включает в себя класс-отправитель.

Описание

Паттерн поведения объектов,известен так же под именем Action(действие).

Обеспечивает обработку команды в виде объекта, что позволяет сохранять её, передавать в качестве параметра методам, а также возвращать её в виде результата, как и любой другой объект.

Например, библиотека печати может иметь класс PrintJob. Для его использования можно создать объект PrintJob, установить необходимые параметры, и вызвать метод, непосредственно отсылающий задание на печать.

Примеры

Пример на С++

Исходный текст на языке C++

class Document { vector 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 _commands = new List();

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

this−>calculator=this->calculator = this>calculator=calculator; this−>operator=this->operator = this>operator=operator; this−>operand=this->operand = this>operand=operand; } /** * Переопределенная функция parent::Execute() */ public function Execute() { this−>calculator−>Operation(this->calculator->Operation(this>calculator>Operation(this->operator, $this->operand); } /** * Переопределенная функция parent::UnExecute() */ public function UnExecute() { this−>calculator−>Operation(this->calculator->Operation(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+=this->curr+=this>curr+=operand; break; case '-': this−>curr−=this->curr-=this>curr=operand; break; case '*': this−>curr∗=this->curr*=this>curr=operand; break; case '/': this−>curr/=this->curr/=this>curr/=operand; break; } print("Текущий результат = this−>curr(послевыполненияthis->curr (после выполнения 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<i < i<levels; $i++) if ($this->current < count($this->commands) - 1) this−>commands[this->commands[this>commands[this->current++]->Execute(); } /** * Функция отмены команд * * @param int $levels количество отменяемых операций */ public function Undo($levels) { print("\n---- Отменить $levels операций "); // Делаем отмену операций for ($i = 0; i<i < i<levels; $i++) if ($this->current > 0) this−>commands[this->commands[this>commands[this->current]->UnExecute(); } /** * Функция выполнения команд * * @param string $operator * @param mixed $operand */ public function Compute($operator, $operand) { // Создаем команду операции и выполняем её this−>command=newCalculatorCommand(this->command = new CalculatorCommand(this>command=newCalculatorCommand(this->calculator, operator,operator, operator,operand); $this->command->Execute(); // Добавляем операцию к массиву операций и увеличиваем счетчик текущей операции this−>commands[]=this->commands[]=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.NET

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

Ссылки

Просмотр этого шаблона Шаблоны проектирования
Основные ДелегированияИнтерфейсНеизменяемый объектФункционального дизайна
Порождающие Абстрактная фабрикаОбъектный пулОдиночкаОтложенная инициализацияПрототипСтроительФабричный метод
Структурные АдаптерВыделение частного класса данныхДекораторЗаместительКомпоновщикМостПриспособленецФасад
Поведенческие ИнтерпретаторИтераторКомандаНаблюдательПосетительПосредникСостояниеСтратегияХранительЦепочка обязанностейШаблонный метод
Блокировка с двойной проверкой • Однопоточное выполнениеПланировщик