Esse artigo faz parte da revista Java Magazine edição 21. Clique aqui para ler todos os artigos desta edição

Atenção: por essa edição ser muito antiga não há arquivo PDF para download.Os artigos dessa edição estão disponíveis somente através do formato HTML. 

Mais Patterns Aplicados

Comportamento e Estruturação de Aplicações

Aumente o nível de reutilização e a flexibilidade dos seus sistemas com os padrões Command,Composite,Decorator e Template Method

 

No artigo “Patterns aplicados” da edição anterior, vimos como incorporar padrões de projeto (design patterns) a código existente,criando a camada de acesso a dados de uma aplicação. Usamos os padrões Data Acess Obejct (DAO), Factory Method,Abstract Factory e Singleton. Mas nem só de acesso a dados vivem as aplicações. É necessário lidar com requisições de usuários, processamento da lógica de negócio e questão como logging,auditoria e segurança,entre muitos outros aspectos.

Neste artigo, são mostrados os padrões Command.Template Method,Composite e Decorator. Como na edição 20,vamos começar com um exemplo simples e acrescentar requisitos que sugiram a introdução dos padrões num contexto de evolução do sistema. Nos quadros “Patterns explicado” são mostrados abordagens alternativas e conseqüências de se adotar cada padrão. Usaremos como exemplo um sistema web de reservas de uma agência de viagens, em que os clientes podem reservar vôos e hotéis e alugar carros no destino. Você notará aqui dois aspectos importantes do uso de padrões de projeto: como eles auxiliam na comunicação entre desenvolvedores, formando um vocabulário expressivo (embora seus nomes, normalmente mantidos em inglês, possam levar ao “portungles”); e como colaboram  formando rede de valor agregado que traz flexibilidade e facilidade de manutenção às aplicações.

 

ü       Não há dependências entre este e o artigo da edição anterior. Além de ser usado um exemplo completamente diferente, os padrões aqui mostrados podem ser aplicados de forma independente dos apresentados na Edição 20. Basta conhecer os conceitos essenciais de padrões para acompanhar este artigo.

 

Versão inicial

Vamos começar com um servlet que atende a todas as solicitações de reserva, sendo os detalhes da solicitação submetidos como parâmetros do request por um formulário web. Esses parâmetros devem incluir o tipo de reserva e o item a ser reservado. Se ocorrer um erro no processamento da reserva, será retornada uma mensagem ao usuário.

Em nome da simplicidade as artes do programa que não forem relevantes ao nosso exemplo são resolvidos por métodos estáticos da classe Util,numa classe “mágica que vai nos permitir focar nos aspectos essenciais do sistema (em uma aplicação “real”,esta funcionalidade estaria distribuída em varias classes). Além disso, trechos poucos importantes ou repetidos serão substituídos por reticências.

O código inicial do servlet de reservas é mostrado na Listagem 1.  Nele a interface Reserva é implementada pelas classes ReservaCarro,ReservaVoo,ReservaHotel. O método agendar() dessas classes faz a reserva do tipo correspondente numa determinada data. Se ocorrer um erro a mensagem poderá ser obtida pelo método getErro() da própria reserva (esta é uma simplificação - em uma aplicação “real” deveriam ser lançadas exceções de negócio”. Veja a organização das classes no diagrama da Figura 1.

 

Figura 1. Classes Iniciais

 

Listagem 1. versão 1:servlet de processamento de reservas

 

public class ServletReservas extends HttpServlet  {

    private static final String PARAM_TIPO = “PARAM_TIPO_RESERVA”;

   

    //... Outros parâmetros

    private static final String ATTR_ERRO = “ATTR_ERRO”;

    private static final String TIPO_RESERVA_VOO = “RESERVA_VOO”;

   

    //... Outros tipos

   

    public void doGet(HttpservletRequest request.

          HttpServletResponse response) throws ServletException. IOException

    {   

       String erro = null;

      

       Cliente cliente = Util.encontractClientePorId(

               request.getParameter(PARAM_CLIENTEID));

       

       String tipoReserva =  request.getParameter(PARAM_TIPO);

      

       /* Verificar tipo de reserva */

       if (tipoReserva.equals(TIPO_RESERVA_VOO)) {

         reserva = new ReservaVoo () ;

       }

       else if (tipoReserva.equals(TIPO_RESERVA_HOTEL)) {

           reserva = new ReservaHotel () ;

       }  

       else if (tipoReserva.equals(TIPO_RESEVA_CARRO)) {
           Integer idade = cliente.getIdade () ;

           if (idade.intValue () >= 18) {

             reserva = new ReservaCarros () ;

           }else {

              erro = “Apenas maiores de 18 anos podem alugar carros.”;

           }

       }

      

       Calendar data = Util.converteParaCalendar(request.getParameter(PARAM_DATA)); 

       if ( ! reserva,agendar (data) ) {

           Request.setAttribute(ATTR_ERRO.reserva.getERRO());

       }

       

       RequestDispatcher rd = request.getRequestDispatcher(“/reservas.jsp”);

rd.forward(request.response);

    }

}

 

Versão 2: usando Commend para encapsular operações

Como sabemos, grande parte do trabalho das aplicaçoes comerciais é lidar com requisições de usuários. No nosso primeiro exemplo isso é feito usando lógica condicional, o que pode dificultar a codificação de novos requisitos à medida que o sistema cresce.

Imagine que, com o crescimento da agência, comecem a surgir novas parcerias,e que toda semana o sistema precise atender a um tipo de reserva como a de restaurantes,passeios turísticos,congressos... Se continuarmos crescendo a árvore de decisão dessa maneira, em pouco tempo teremos uma classe com milhares de linhas. E como a lógica de reservas pode ser arbitrariamente complexa, imagine se as reservas de restaurantes, vôos e hotéis precisassem simultaneamente de correções. Mesmo tendo um bom sistema de controle de versões o sistema se tornaria cada vez mais difícil de manter com essa abordagem.

Vamos então encapsular cada solicitação do usuario em um objeto separado, usando o padrão de projeto Command. Depois de separar os tipos de reserva em “comandos”,usaremos uma fábrica para escolher qual comando criar,retirando esta decisão do servlet¹. Assim, conseguimos isolar o servlet que atende aos pedidos dos usuários da lógica envolvida em cada tipo de reserva. A solução é apresentada na Figura 2. O código do servlet (Listagem 2) fica mais simples e não precisará mais ser alterado se forem adicionados novos tipos de reservas. O método extrairParametros() de ServletReservas,omitido na listagem,apenas copia parâmetros do request para o Map de parâmetros do comando a ser executado.

Veja mais sobre o usso desse padrão no quadro “Patterns explicado: Command”.

 

ü       As referências entre colchetes nos quadros, como [GoF] e [J2EE] indicam catálogos de padrões de projetos. A maioria desses padrões está documenatada, com exemplos, em vários websites (veja links).

 

...

Quer ler esse conteúdo completo? Tenha acesso completo