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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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#
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]Objetos command são usados para implementação.
GUI botões e itens de menu
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]É 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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]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
[editar | editar código-fonte]Também conhecido como Action (Ação), Transaction (Transação).
também conhecido como VGA