Aprenda nesse artigo a adicionar flexibilidade a seus sistemas mantendo o baixo acoplamento e fácil manutenção criando extensões CDI.
Artigos Java Como criar extensões CDI
Mentoring:Este artigo visa mostrar de forma prática o uso e a criação de extensões CDI. Como exemplo, será refatorada uma simples aplicação web que deve se comportar de uma maneira diferente de acordo com as características específicas do servidor em que for distribuída.
A característica que será considerada no artigo é a localização geográfica, ou região.
Note que é possível atender a esse requisito sem utilizarmos o poder da injeção de dependências e da inversão de controle, porém, deste modo, podem ser criadas soluções que não fazem um bom uso da orientação a objetos, perdendo em flexibilidade, manutenibilidade, adaptabilidade e até mesmo em legibilidade.
Em contrapartida, com o uso de extensões CDI conseguimos sanar o problema viabilizando uma aplicação com baixo acoplamento e de fácil manutenção.
Existem situações em que é preciso que o sistema seja parametrizado para permitir que seu funcionamento seja alterado sem a necessidade de compilação ou realização de um novo deploy. Para isso, muitas vezes são criados arquivos de configuração que definem o comportamento do sistema, regras de acesso a funcionalidades, log, etc.
Geralmente, esses arquivos possuem conteúdo diferente para ambientes de desenvolvimento e produção, ficando a cargo do desenvolvedor tomar o devido cuidado para não confundir os arquivos e não realizar o deploy da aplicação com o arquivo errado.
Observe que, dependendo da situação, podemos diminuir a complexidade do processo de deploy criando scripts ou soluções mais dinâmicas que determinem o comportamento do sistema em tempo de execução.
Considerando uma aplicação que possua acesso a dados ou parâmetros que possam ser usados para definir o comportamento da mesma de forma dinâmica, ainda assim é necessário que tomadas de decisão sejam definidas no código fonte.
Estas tomadas de decisão podem ser escritas através de simples ifs ou utilizando polimorfismo em conjunto com boas práticas de orientação a objetos.
Já com o uso da injeção de dependências, simplificamos a construção de um bom design orientado a objetos, o que pode ser viabilizado ao adicionar CDI ao projeto.
Através do CDI, mais precisamente a partir de extensões, é possível implementar uma solução para o cenário citado sem a necessidade de escrever vários ifs no código.
Deste modo, para exemplificar a criação de extensões CDI, este artigo apresentará uma aplicação web envolvida em um contexto que traz como importante requisito a necessidade de implementação de uma solução dinâmica que não faça uso de arquivos de configuração. Esta aplicação encontra-se disponível no repositório online Bitbucket com o nome artigo-cdi (veja o endereço na seção Links). O Bitbucket trata-se de um serviço de hospedagem de projetos que podem ser gerenciados através de sistemas de controle de versão como Git e Mercurial.
A aplicação criada para o artigo trata-se de uma loja de livros virtual fictícia que deve atender a usuários em diferentes regiões geográficas, sendo necessário aplicar regras distintas para cálculo de impostos, ou seja, é de grande importância que a aplicação seja flexível. Mais detalhes sobre o seu funcionamento serão explicados no próximo tópico.
Dito isso, para conseguir alcançar a flexibilidade necessária e atender as diferentes regiões do mundo, suponha que foi sugerida a utilização da computação em nuvem, de modo que o serviço contratado ofereça meios para identificação da região através de uma API própria.
Como exemplo de empresa que oferece serviços de computação em nuvem, vamos considerar a Amazon, empresa que disponibiliza servidores virtuais distribuídos em diferentes regiões geográficas.
Estes servidores são oferecidos como instâncias EC2 (Elastic Compute Cloud). Dessa forma, o desenvolvedor pode criar sistemas de alta disponibilidade e aumentar a escalabilidade contratando mais instâncias de acordo com a demanda.
Esse é um exemplo de computação em nuvem entregue como IaaS, ou Infrastructure as a Service (Infraestrutura como serviço), e que também é oferecido por outras empresas como a DigitalOcean e a Microsoft, com o Microsoft Azure.
Muitos desses serviços oferecidos na nuvem permitem recuperar dados sobre a região da instância sendo utilizada através de API própria.
Na Amazon, por exemplo, quando se cria uma instância EC2, é necessário informar em qual região se deseja hospedar a aplicação. Para permitir esse tipo de funcionalidade, a Amazon define dois conceitos importantes, conhecidos como Region and Availability Zone.
Baseando-se neles é fornecida uma API que permite ao desenvolvedor descobrir, em tempo de execução, qual região está sendo atendida.
No exemplo adotado para este artigo, serão atendidas duas regiões: América do Norte e a região de São Paulo, lembrando que para atender diferentes regiões, não basta realizar apenas a internacionalização de textos.
Algumas vezes é necessário também atender a requisitos específicos, como diferentes regras para cálculo de impostos, serviços de postagem, regras para compra e venda, etc.
Já na versão inicial, o sistema utiliza uma API que simula o serviço oferecido pela Amazon para recuperação dos dados da região corrente. Porém, aplica as regras específicas para cada região em tempo de execução através de if/else, dentro de uma mesma unidade de código (uma classe Java, por exemplo). Os problemas trazidos por essa abordagem incluem alto acoplamento, código de difícil manutenção, baixa legibilidade e alto índice de complexidade ciclomática.
Para resolver os problemas descritos serão apresentadas duas possíveis soluções utilizando CDI. A primeira delas separa as implementações das regras específicas em classes diferentes e as anota com @Alternative, permitindo ao desenvolvedor escolher, em tempo de desenvolvimento, qual implementação deve ser utilizada através do arquivo XML beans.xml, que, por sua vez, deve ser criado dentro do diretório WEB-INF da aplicação (por tratar-se de uma aplicação web).
A partir desse arquivo é possível habilitar quais beans estarão disponíveis para injeção durante a inicialização da aplicação. Já a segunda solução demonstra uma implementação mais dinâmica, que faz uso da extensão CDI que será criada neste artigo.
Com essa extensão o desenvolvedor não precisa definir no arquivo beans.xml quais beans devem estar disponíveis para cada região, poupando a equipe da necessidade de manter diferentes versões do arquivo.
Descrição inicial do projeto
A loja virtual de livros utilizada como exemplo possui os seguintes requisitos:
· O usuário pode selecionar um ou mais livros para compra;
· O usuário deve confirmar um pedido após solicitar a compra de livros;
· O sistema deve apresentar o valor total do pedido antes que o usuário confirme a compra;
· O valor total do pedido é calculado somando-se o valor dos itens mais o valor do imposto, que pode variar de acordo com a região atendida. Para o Oeste dos EUA, será cobrada uma taxa de 4% e para São Paulo, será cobrada uma taxa de 15%. Todos esses detalhes da aplicação podem ser verificados no código fonte do projeto.
Em cada etapa da evolução da aplicação, o problema da região será resolvido de uma forma diferente, permitindo que o leitor perceba as vantagens da utilização de extensões CDI. Para que o leitor possa baixar os exemplos considerando cada cenário ou solução, foram criadas tags através do Git (disponíveis no Bitbucket) específicas para cada abordagem, como pode ser visto na Tabela 1.
versao-simples
Abordagem utilizando estruturas simples de controle (if/else).
versao-alternative
Solução utilizando @Alternative para injetar a calculadora correta para cada região. A implementação da calculadora a ser utilizada é definida no arquivo beans.xml.
versao-extensions
Solução utilizando extensões CDI.
Tabela 1. Abordagens utilizadas para adicionar flexibilidade ao sistema.
O container web utilizado para executar a aplicação foi o Tomcat versão 7. A Figura 1 exibe uma visão macro do fluxo básico de navegação da aplicação, onde o usuário seleciona dois livros e, ao confirmar a compra, é levado para a segunda tela, que contém o valor total do pedido com os impostos devidamente calculados.
Neste exemplo, ao comprar dois livros, nos valores de 50,00 e 158,27, o usuário terá que pagar 208,27 mais 4% referente a impostos, totalizando o valor de 216,6008. Note que não utilizamos unidade monetária para simplificar os exemplos de código disponíveis no artigo.