Command
Command é um dos 11 padrões comportamentais dentre os 23 padrões de projeto de software do GOF. Na programação orientada a objeto, o command é um padrão no qual um objeto é usado para encapsular toda informação necessária para executar uma ação ou acionar um evento em um momento posterior.
Objetivo
O Padrão Command tem como definição encapsular uma solicitação como um objeto, o que lhe permite parametrizar outros objetos com diferentes solicitações, enfileirar ou registrar solicitações e implementar recursos de cancelamento de operações. Isso inclui informações como o nome do método, o objeto que o método pertence e os valores dos parâmetros do método.
Estrutura
![Estrutura-command](http://upload.wikimedia.org/wikipedia/commons/a/a7/Estrutura-command.png)
A classe aplicação cria um comando concreto e configura o receiver para que este possa executá-lo. A classe Receiver, que pode ser qualquer classe na aplicação, sabe como executar o trabalho necessário. ConcreteCommand é responsável por manter um vínculo entre uma ação (método action()) e um objeto da classe Receiver. Assim, um objeto Invoker invoca o método execute() e ConcreteCommand realiza uma ou mais ações no objeto Receiver. O papel do Command, que pode ser uma interface ou classe abstrata, é o de disponibilizar uma interface comum a todas as classes concretas que a implementam.
Implementação
Considere um "simples" interruptor. Nesse exemplo vamos configurar o interruptor (Switch) com duas funções: ligar e desligar a luz.
Um benefício em particular da implementação do command pattern é que o interruptor pode ser usado em qualquer dispositivo, não somente uma luz - no próximo exemplo, o Switch liga e desliga a luz, mas o construtor do Switch aceita qualquer subclasse de Command para seus dois parametros. Por exemplo, você pode configurar o Switch (interruptor) para ligar um motor.
C#
O código a seguir é uma implementação do Command Pattern em C#.
namespace CommandPattern { using System; public interface ICommand { void Execute(); } /* Classe invocadora */ public class Switch { ICommand _closedCommand; ICommand _openedCommand; public Switch(ICommand closedCommand, ICommand openedCommand) { this._closedCommand = closedCommand; this._openedCommand = openedCommand; } //fecha o circuito/liga public void Close() { this._closedCommand.Execute(); } //abre o circuito/desliga public void Open() { this._openedCommand.Execute(); } } /* Uma interface que define as ações que o recebedor consegue executar*/ public interface ISwitchable { void PowerOn(); void PowerOff(); } /* Classe recebedora */ public class Light : ISwitchable { public void PowerOn() { Console.WriteLine("A luz está ligada"); } public void PowerOff() { Console.WriteLine("A luz está desligada"); } } /* O comando para ligar o dispositivo - ConcreteCommand #1 */ public class CloseSwitchCommand: ICommand { private ISwitchable _switchable; public CloseSwitchCommand(ISwitchable switchable) { _switchable = switchable; } public void Execute() { _switchable.PowerOn(); } } /* O comando para desligar o dispositivo - ConcreteCommand #2 */ public class OpenSwitchCommand : ICommand { private ISwitchable _switchable; public OpenSwitchCommand(ISwitchable switchable) { _switchable = switchable; } public void Execute() { _switchable.PowerOff(); } } /* Classe teste */ internal class Program { public static void Main(string[] args) { string arg = args.Length > 0 ? args[0].ToUpper() : null; ISwitchable lamp = new Light(); //Passa referencia da lampada para cada comando ICommand switchClose = new CloseSwitchCommand(lamp); ICommand switchOpen = new OpenSwitchCommand(lamp); //Passa referencia para as instancias dos objetos do Command para o Switch Switch @switch = new Switch(switchClose, switchOpen); if (arg == "ON") { // Switch (Invocador) vai invocar o Execute() (the Command) no objeto do command - _closedCommand.Execute(); @switch.Close(); } else if (arg == "OFF") { //Switch (Invocador) vai invocar o Execute() (the Command) no objeto do command - _openedCommand.Execute(); @switch.Open(); } else { Console.WriteLine("Argumento \"ON\" ou \"OFF\" é necessário."); } } } }
Java
import java.util.List; import java.util.ArrayList; /** Interface do Command */ public interface Command { void execute(); } /** Classe invocadora */ public class Switch { private List<Command> history = new ArrayList<Command>(); public void storeAndExecute(Command cmd) { this.history.add(cmd); // optional cmd.execute(); } } /** Classe recebedora */ public class Light { public void turnOn() { System.out.println("A luz está ligada"); } public void turnOff() { System.out.println("A luz está desligada"); } } /** O Command para ligar a luz - ConcreteCommand #1 */ public class FlipUpCommand implements Command { private Light theLight; public FlipUpCommand(Light light) { this.theLight = light; } @Override // Command public void execute() { theLight.turnOn(); } } /** O Command para desligar a luz - ConcreteCommand #2 */ public class FlipDownCommand implements Command { private Light theLight; public FlipDownCommand(Light light) { this.theLight = light; } @Override // Command public void execute() { theLight.turnOff(); } } /* Classe teste */ public class PressSwitch { public static void main(String[] args){ // Checa o número de argumentos if (args.length != 1) { System.err.println("Argumento \"ON\" or \"OFF\" é necessário."); System.exit(-1); } Light lamp = new Light(); Command switchUp = new FlipUpCommand(lamp); Command switchDown = new FlipDownCommand(lamp); Switch mySwitch = new Switch(); switch(args[0]) { case "ON": mySwitch.storeAndExecute(switchUp); break; case "OFF": mySwitch.storeAndExecute(switchDown); break; default: System.err.println("Argumento \"ON\" or \"OFF\" é necessário."); System.exit(-1); } } }
Problema
Algumas vezes é necessário emitir solicitações para objetos nada sabendo sobre a operação que está sendo solicitada ou sobre o receptor da mesma.
Utilizar quando:
- Parametrizar objetos por uma ação a ser executada. Você pode expressar tal parametrização numa linguagem procedural através de uma função callback, ou seja, uma função que é registrada em algum lugar para ser chamada em um momento mais adiante. Os Commands são uma substituição orientada a objetos para callbacks;
- Especificar, enfileirar e executar solicitações em tempos diferentes. Um objeto Command pode ter um tempo de vida independente da solicitação original. Se o receptor de uma solicitação pode ser representado de uma maneira independente do espaço de endereçamento, então você pode transferir um objeto Command para a solicitação para um processo diferente e lá atender a solicitação;
- Suportar desfazer operações. A operação Execute, de Command, pode armazenar estados para reverter seus efeitos no próprio comando. A interface do Command deve ter acrescentada uma operação Unexecute, que o reverte.efeitos de uma chamada anterior de Execute. Os comandos executados são armazenados em uma lista histórica. O nível ilimitado de desfazer e refazer operações é obtido percorrendo esta lista para trás e para frente, chamando operações Unexecute e Execute, respectivamente.
Aplicabilidade
A chave deste padrão é uma classe abstrata Command, a qual declara uma interface para execução de operações. Na sua forma mais simples, esta interface inclui uma operação abstrata Execute. As subclasses concretas de Command especificam um par receptor-ação através do armazenamento do receptor como uma variável de instância e pela implementação de Execute para invocar a solicitação. O receptor tem o conhecimento necessário para poder executar a solicitação, porém externamente os objetos não sabem quais ações estão sendo executadas no receptor, eles apenas visualizam o método execute() que irá executar as suas solicitações. Desta forma, todos os clientes de objetos command tratam cada objeto como uma "caixa preta", simplesmente invocando o método execute()sempre que o cliente exige "serviço" do objeto.
Usar objetos de Command faz com que seja mais fácil construir componentes gerais que precisam delegar, sequenciar ou executar chamadas de métodos em um momento de sua escolha, sem a necessidade de conhecer a classe do método ou os parâmetros do método. Usar um objeto invoker permite contabilizar sobre as execuções de comando a serem realizadas convenientemente, bem como a implementação de diferentes modos para comando, que são geridos pelo objeto invoker, sem a necessidade do cliente estar ciente da existência da contabilidade ou modos.
Usos
Objetos command são usados para implementação.
GUI botões e itens de menu
Em Swing e programação Borland Delphi, uma ação é o objeto command. Além da capacidade de executar o comando desejado, uma ação deve ser associada a um ícone, teclado, atalho e assim por diante. Um botão da barra de ferramentas ou menu pode ser completamente inicializado usando apenas o objeto Action.
Gravações de Macros
Se todas as ações são representadas pelos objetos command, o programa pode gravar uma sequência de ações simplesmente mantendo uma lista dos objetos command como eles são executados. Pode-se então “Reproduzir” as mesmas ações, executando os mesmo objetos do comando novamente em sequência. Se o programa incorpora um mecanismo de script, cada objeto de comando pode implementar um método toScript() e as ações do usuário podem então ser facilmente registradas como scripts.
Código Mobile
Usando linguagens como Java onde o código pode ser transmitido de um local para o outro através carregadores de classes de URL e codebases os comando podem ativar o novo comportamento a serem entregues para locais remotos (EJB Command, Mester Worker).
Undo Multi-level
Se todas as ações de um programa são implementadas como objetos de comando, o programa pode manter uma pilha dos comandos mais usados recentemente. Quando o usuários precisar desfazer um comando, o programa simplesmente mostra o objeto de comando mais recente e executa o método desfazer.
Networking
É possível enviar todos os objetos command pela network para eles serem executados em outras maquinas. Por exemplo, as ações de um jogador em um jogo de computador.
Processamento Paralelo
Onde os comandos são escritos como tarefas a um recurso compartilhado e executado por muitos segmentos em paralelo (possivelmente em maquinas remotas – Está variante é muitas vezes referida como o padrão Master/Worker.
Barras de progresso
Suponha que um programa tem uma sequência de comandos que ele executar em ordem. Se cada objeto de comando tem um método getEstimateDuration() o programa pode facilmente estimar a duração total. Ele pode mostrar uma barra de progresso que reflete significativamente o quão perto o programa esta da conclusão de todas as tarefas.
Comportamento Transacional
Similar ao undo, um instalador de banco de dados ou software pode manter uma lista de operações que tenham sido realizadas ou venham a ser realizadas. No caso de um deles, falhar todos os outros podem ser revistos ou descartados (normalmente chamado de reversão). Por exemplo, se duas tabelas de banco de dados que se referem uma a outra devem ser atualizadas, e a segunda atualização falhar, a transação pode ser revertida, de modo que a primeira tabela não contenha agora uma referência válida.
Wizard
Muitas vezes, um assistente apresenta várias páginas de configurações para uma única ação que só acontece quando o usuário clica no botão “Finish” na última página. Nestes casos, uma maneira natural de código de interface de usuário separada do código de aplicativo é, implementar o assistente usando um objeto de comando. O objeto de comando é criado quando o assistente é exibido pela primeira vez. Cada página do assistente armazena suas mudanças GUI no objeto de comando, de modo que o objeto é preenchido quando o usuário avança. “Finish” simplesmente dispara uma chamada para executar(). Desta forma, a classe comando irá funcionar.
Anti-Pattern
Classes que implementam métodos para realizarem suas tarefas tornam-se "inchadas", sendo difíceis de receberem manutenção e pouco extensíveis. Com isto, tais classes passam a ter um forte acoplamento com as tarefas (comandos) a serem realizadas.
Sinônimos
Também conhecido como Action (Ação), Transaction (Transação).
também conhecido como VGA
Padrões Relacionados
Composite, Memento, Prototype
Fontes
- Padrões GoF[1] - Acessado em 01 de Outubro de 2016.
- Command Design Pattern, [2] - Acessado em 01 de Outubro de 2016.
- FREITAS, Romualdo Rubens de. Padrões de projeto v3: Catálogo GoF - Catálogo GOF.pdf
- Command, [3] - Acessado em 01 de Outubro de 2016.