terça-feira, 27 de março de 2012

Padrão Command

Salve galera!!

Agora escrevendo sobre nossos queridos Design Patterns, ou Padrões de Projetos... como preferirem. Nesse post vou tentar demonstrar um uso bem interessante para o padrão Command. Talvez o objetivo desse padrão seja outro, mas desde que comecei a usar com o objetivo de tratamentos de exceções, achei muito pratico, produtivo, coeso e desacoplado. Hoje uso esse padrão em aplicações web, desktop, mobile.. ou seja, em todos os projetos que coloco a mão, aplico esse padrão... Então, vamos ao que interessa..

Uma pequena introdução:

Objetivo: Encapsular uma solicitação como um objeto, desta forma permitindo que clientes parametrizem diferentes solicitações, enfileirem ou façam o registro (log) de solicitações e suportem operações que podem ser desfeitas.

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.

Aplicação: 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.

Fonte: http://pt.wikipedia.org/wiki/Command

Agora na pratica, aplicando ele nos meus problemas

Objetivo: Realizar chamada a métodos que lançam exceções, de maneira que todas sejam tratadas em um unico local.

Problema: Quando se pensa na arquitetura de uma aplicação, ou no design, logo pensamos em como vamos tratar as exceções. Então, criamos super exceções para cada camada, ou apenas para camada DAO, e sub-exceções para exceções mais espacificas. Exemplo:

DAOException - Super-exceção
UniqueKeyViolationException - Sub-exceção
PrimaryKeyViolationException  - Sub-exceção
ValidationException - Sub-exceção

Agora temos todas essas exceções para tratarmos com nossos try catch, por toda camada visual, onde vamos consumir os dados da camada BO => DAO ou DAO. Ainda teremos um pequeno problema se uma nova exceção for adicionada a lista, pois ela precisará ser tratada.

Aplicação:  Criar uma classe Chamada CommandDelegate, onde através de um método executar possamos realizar o tratamento de exceções de toda a aplicação de maneira padronizada.

Vamos ao que interessa.. Código fonte!

Interface Command

/**
 * Interface que representa uma operação que
 * deve ter suas exceções tratadas
 * 
 * @author Ricardo Bocchi
 */
public interface Command {

    /**
     * Encapsula um método que deve ser tratado
     * 
     * @throws Exception 
     */
    void operation() throws Exception;
}

Implementação da classe CommandDelegate

/**
 * Exemplo do padrão Command para o tratamento centralizado de exceções 
 *  
 * @author Ricardo Bocchi
 */
public class CommandDelegate {

    private static final Logger logger = Logger.getLogger(CommandDelegate.class.getName());
    private boolean fail;

    /**
     * Executa uma operação que deve ter tratamento de exceções
     * 
     * @param cmd Operação a ser executada
     */
    public void execute(Command cmd) {

        this.fail = true;

        try {
            cmd.operation();
            this.fail = false;
        } catch (PrimaryKeyViolationException e) {
            //Tratar exceção de violação de chave primaria
        } catch (ValidationException e) {
            //Tratar exceção de validação
        } catch (DAOException e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
            //Tratar exceção de banco de dados generica
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
            //Tratar exceção generica
        }
    }

    public boolean isFail() {
        return fail;
    }
}

Uso

        new CommandDelegate().execute(new Command() {

            @Override
            public void operation() throws Exception {
                dao.executeThrowsOperation()
            }
        });

Esse é o uso mais simples possivel. Ainda podemos identificar os seguintes problemas:

Se ocorrer um erro, como posso parar a execução se as exceções estão sendo "caladas" dentro do CommandDelegate? Para identificar erros, podemos chamar o método isFail(), que retorna se ocorreu algum erro na execução. Poderimos altera o método execute na classe CommandDelegate, para:


    public CommandDelegate execute(Command cmd) {

        this.fail = true;

        try {
            cmd.operation();
            this.fail = false;
        } catch (PrimaryKeyViolationException e) {
            //Tratar exceção de violação de chave primaria
        } catch (ValidationException e) {
            //Tratar exceção de validação
        } catch (DAOException e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
            //Tratar exceção de banco de dados generica
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
            //Tratar exceção generica
        }

        return this;
    }

E depois usar assim
        
       CommandDelegate delegate = new CommandDelegate().execute(new Command() {

            @Override
            public void operation() throws Exception {
                dao.executeThrowsOperation()
            }
        });

        if(delegate.isFail()){
          //Ocorreu erro! 
          return;
        }

E se eu precisar passar parametros para o método ou pegar o retorno?
Nesse no caso de parametro, se ele fosse final, não teria problemas. E caso ele não possa ser final, poderiamos criar uma classe Wrapper generica para as chamadas.

Classe Wrapper
    
/**
 * Classe Wrapper
 *  
 * @author Ricardo Bocchi
 */
public class Wrapper {

    public T obj;
}

E usar
   
       Wrapper wrapper = new Wrapper();
     
       CommandDelegate delegate = new CommandDelegate().execute(new Command() {

            @Override
            public void operation() throws Exception {
                wrapper.obj = dao.loadCliente(1);
            }
        });

        if(delegate.isFail()){
          //Ocorreu erro! 
          return;
        }


Então, apresentei aqui um uso interessante para o padrão Command já prevendo algumas complicações que ele geraria. Ainda poderiamos realizar diversas customizações, de modo a facilitar o uso do padrão.

E por hora era isso.. até a próxima! abrass

Nenhum comentário:

Postar um comentário