terça-feira, 14 de agosto de 2012

Utilizando Design Patterns: Command

Através do padrão Command é possível encapsular operações em objetos. Ele é utilizado em casos onde é necessário evitar o acoplamento entre o objeto que invoca (Invoker) a operação e o objeto que implementa (Receiver) a operação. O padrão Command possui, em geral, cinco elementos importantes - destacados abaixo:

Client
O cliente realiza a chamada
(instancia) dos métodos do Invoker.

Invoker
Ele será o beneficiado direto do recurso. Invoker possui um atributo de Command (existem variações, porém vou explicar um caso geral). No constructor de Invoker, o valor desse atributo será definido. Quando Invoker for instanciado, ele deverá conter algum objeto que implemente de forma concreta a interface Command. Além disso, Invoker também possui um método que executa o método execute() de Command, porém ele não sabe o que será executado. Esse é o ponto forte desse padrão. Invoker precisa executar alguma coisa no qual não conhece e não possui acoplamento. Veremos um exemplo prático.

Receiver

Em termos gerais, esta é uma instância da classe que contém a implementação da operação. Esse é o objeto que possui o código da operação em si. Qualquer classe pode funcionar como um Receiver. Na prática, uma classe (ConcreteCommand) implementará a interface Command e, por consequência, implementará o método execute(). Nesse método será trabalhado algum comportamento de Receiver.

Command
Interface que implementa a assinatura dos métodos utilizados. No mínimo ela deverá implementar um método chamado execute().

ConcreteCommand
Esta classe implementa a interface Command e define o método execute(). Deve-se atentar que o comportamento do comando tem origem em Receiver.

Para implementar um Command, precisamos de:

  1. Criar uma interface chamada Command com o método execute()
  2. Criar a classe Receiver que implementa os comportamentos reais
  3. Criar a classe que implementa Command, encapsula os métodos de Receiver no método execute()
  4. Criar a classe Invoker, que possui um elemento Command e métodos que invocam execute()
  5. Criar a classe Client que trabalha com as opções de Invoker

Para melhor exemplificar a implementação, observe o diagrama abaixo:


Como mostrado no diagrama de classe acima, a interface Command define o protótipo do comando execute(). Esse método é criado na classe ConcreteCommand que executa o método action() do Receiver. Observe ainda que Invoker é constituído por Command. Ao definirmos uma ConcreteCommand no atributo command de Invoker, estaremos associando esse objeto a um comportamento no qual ele não conhece.
Vamos agora a um exemplo prático da utilização deste padrão.

Exemplo prático
Imagine a seguinte situação: existem duas equipes de desenvolvimento de um projeto. A equipe 1 é responsável pela camada de negócios. A equipe 2 fará o desenvolvimento da camada de aplicação (interface com usuário). A equipe 2 irá desenvolver uma Frame com 2 botões. A questão é a seguinte: O que ocorre quando o botão for acionado? A equipe 2 não sabe! Se botão possuir um atributo da interface Command, ele poderá executar operações diferentes de acordo com o comportamento criado nas classes concretas de Command. Da mesma forma, a equipe 1 não sabe a partir de qual botão serão acionados os métodos de negócio. O que ela faz é criar um encapsulamento das operações como uma implementação concreta da interface Command. Quando a classe Client (um dos elementos do padrão Command) for criada, a associação entre esses comportamentos será definida.



Vamos ao código:
A codificação da interface Command é mostrada abaixo.

Command.java

package engine;

public interface Command {
    
    public void execute();

}

Veremos agora a classe Button, referente ao Invoker.

Button.java

package engine;

public class Button {
  
    private Command command;
  
    public Button(Command command) {
        this.command = command;
    }
  
    public void clicked() {
        this.command.execute();
    }

}


Veja que esta classe possui um atributo de Command que será definido na criação do objeto. Ao executar clicked(), o método execute() será acionado. Isso faz com que o botão não tenha conhecimento da ação efetuada em seu clique. Vamos ver agora a classe associada ao Receiver, chamada Business. Em nosso estudo, a equipe 2 foi responsável pela criação de Button.java.

Business.java

package engine;

public class Business {

    public void findData() {
        System.out.println( "Procurando dados!" );
    }
    
    public void saveData() {
        System.out.println( "Salvando dados!" );
    }
    
}

Nessa classe os métodos de negócio são definidos. Em nosso exemplo, a equipe 1 foi responsável pela criação desta classe. Agora esses comportamentos precisam ser encapsulados em objetos. Abaixo temos as classes FindData e SaveData, ambos criados pela equipe 1.

FindData.java

package engine;

public class FindData implements Command {

    private Business receiver;
    
    public FindData(Business receiver) {
        this.receiver = receiver;
    }
    
    public void execute() {
        this.receiver.findData();
    }
    
}

Observe que o método real findData() de Business foi chamado em execute().

SaveData.java

package engine;

public class SaveData implements Command {

    private Business receiver;
    
    public SaveData(Business receiver) {
        this.receiver = receiver;
    }
    
    public void execute() {
        this.receiver.saveData();
    }
    
}

De forma semelhante, execute() acionará o método saveData() de Business. Por fim, veremos agora o objeto Frame. Este será responsável pela configuração do comportamento dos botões. Ele é associado ao objeto Client do pattern Command. Veja seu código abaixo:

package engine;

public class Frame {

    private Business receiver;
    
    public Frame(Business receiver) {
        this.receiver = receiver;
    }
    
    public Business getReceiver() {
        return this.receiver;
    }
    
    public static void main(String args[]) {
        
        Frame frame = new Frame( new Business() );
        
        Command findData = new FindData( frame.getReceiver() );
        Command saveData = new SaveData( frame.getReceiver() );
        
        Button findButton = new Button( findData );
        Button saveButton = new Button( saveData );
        
        findButton.clicked();
        saveButton.clicked();
        
    }
    
}

Vamos entender o que ocorre. Frame é uma interface com usuário. Quando os botões foram criados (codificação da classe Button), suas ações ainda não eram previstas - ou seja, um botão é genérico. Ao conter uma interface Command, o Button poderá executar o método de acordo com o comportamento definido nas classes concretas FindData e SaveData. Veja no código anterior que são criados dois botões. O primeiro deles, quando clicado, executa o comportamento FindData - que na verdade é a operação findData de Business encapsulada num objeto. O mesmo ocorre para saveButton.

Conclusões
Ao utilizar padrões como esse, estamos favorecendo atributos como escalabilidade da aplicação. O reaproveitamento de código é facilitado e existe também ganho de desenvolvimento levando em consideração o tempo. Por fim, é uma boa prática de programação a utilização de patterns como esse.

Referências
http://www.dotnetraptors.com.br/start/artigos/artigos_open_space/5679.aspx
http://en.wikipedia.org/wiki/Command_pattern
http://sourcemaking.com/design_patterns/command
http://www.dsc.ufcg.edu.br/~jacques/cursos/map/html/pat/command.htm


"Esforce-se para viver ou esforce-se para morrer
!" Stephen King (livro Quatro Estações)
Espero ter ajudado!
Guilherme Pontes

Nenhum comentário:

Postar um comentário