Como implementar Pattern Facade na nova Plataforma Java EE

Veja neste artigo o que é o padrão de projeto Facade, quais suas vantagens e desvantagens e como podemos implementar este padrão na nova plataforma Java EE 7. Também veremos diferentes as formas de implementar o padrão de projeto Facade.

A plataforma Java EE 7 simplificou ainda mais a forma de programarmos aplicações corporativas. As anotações têm substituído os arquivos descritores XML, configurações default foram incorporadas, e a injeção de dependência simplificou significativamente o código que antes utilizava a criação e o lookup de recursos diretamente no código, tornando-o mais complexo. Assim, o desenvolvimento de aplicações empresariais tem se tornado cada vez mais fácil com a plataforma Java EE, visto que precisamos apenas de um POJO (Plain Old Java Object) e algumas anotações nesse Pojo.

Entre as novidades da plataforma ainda destacam-se a JPA 2.1 que suporta chamadas a procedures de forma transparente, CDI 1.1 que possui algumas mudanças e melhorias incluídas nessa versão como a criação de uma API para criação de contextos customizados, injeção de objetos estáticos e criação de eventos na API Servlet, melhorias incluídas no EJB 3.2 que facilitam ainda mais as configurações e uso de EJBs como o super e transações dentro de callbacks do ciclo de vida dos beans, adições na JSF 2.2 que estavam disponíveis através do Seam e agora fazem parte da especificação oficial JSR-344 assim como a adição de anotações e integração com HTML 5, novas funcionalidades e melhorias na JAX-RS 2.0 como a integração com Bean Validation, diversas alterações e adições de funcionalidades na JMS 2.0 removendo muitos códigos desnecessários para o envio de mensagens, melhorias no Bean Validation 1.1 e a adição de outras APIs que estavam sendo solicitadas há muitos anos pelos desenvolvedores como a JCACHE, Batch para executar tarefas em lote, JSON-P, entre outras. Mais informações sobre a Java EE 7 são encontradas no portal devmedia em que forneço diversos detalhes sobre as novas APIs e funcionalidades incorporadas na plataforma.

Apesar da evolução da plataforma Java EE os padrões de projeto ainda são essenciais para o sucesso de um projeto de desenvolvimento. Padrões de projeto são soluções já testadas e documentadas para um problema, permitindo assim reutilizarmos a experiência de outros desenvolvedores que já tiveram os mesmos problemas ou problemas semelhantes. De forma geral, um padrão de projeto possui a descrição de uma solução que foi utilizada para resolver um problema. O objetivo principal dos padrões de projeto é ajudar os desenvolvedores a estruturarem os seus códigos de maneiras mais flexíveis, fáceis de entender e simples de manter. Isso não é uma tarefa fácil quando possuímos uma plataforma de software no meio, que muitas vezes dita as regras de como as coisas devem ser implementadas ou como devem funcionar.

Neste artigo veremos um dos padrões de projetos mais utilizados e úteis que é o padrão de projeto Facade. Veremos o que é e como ele é comumente implementado como um Pojo e como podemos implementá-lo na plataforma Java EE que muitas vezes causa confusões nos desenvolvedores de como implementar um padrão bastante utilizado dentro de uma plataforma robusta.

Padrão de Projeto Facade

O padrão de projeto Facade é um padrão estrutural inicialmente descrito no famoso livro do GoF. A intensão deste padrão é encapsular uma lógica complicada em uma interface de alto nível que faz o acesso a um subsistema, tornando-a muito simples e fácil de utilizar. Isto é feito agrupando-se chamadas de métodos e invocando esses métodos sequencialmente através de um único método. Por isso que toda API (Application Programming Interface) pode ser considerada uma implementação do padrão de projeto Facade, visto que as APIs fornecem uma simples interface que esconde a complexidade, onde qualquer chamada para um método da API resulta na invocação de muitos outros métodos do subsistema que está por trás.

Assim sendo, o padrão de projeto Facade é frequentemente implementado para fornecer um acesso simples e unificado a sistemas legados, criar uma API pública para classes (como os drivers), combinar serviços e oferecer um acesso único de granulação grossa, reduzir as chamadas de rede, encapsular o fluxo e detalhes internos de uma aplicação para segurança e simplicidade.

Segue na Figura 1 o diagrama de classes do padrão de projeto Facade.

Figura 1. Diagrama de classes do padrão Facade.

Podemos verificar que tudo que o cliente precisa é realizar o contato diretamente com o Facade, que se encarrega de esconder códigos legados ou bibliotecas de terceiros. Segue na Figura 2 o diagrama de sequencia de como o padrão é usado pelo cliente:

Figura 2. Diagrama de Sequencia do padrão de projeto Facade.

O diagrama de sequência acima ilustra as interações realizadas pela camada Facade após receber uma determinada requisição de um cliente.

O diagrama de classe na Figura 3 representa o padrão Facade sob a perspectiva do GoF.

Figura 3. Diagrama de classes sob a perspectiva dos autores do GoF.

Neste diagrama pode ser verificado alguns papéis como o pacote com o nome "Biblioteca" onde o pacote que incluir os subsistemas e o Facade que acessa os subsistemas. Além disso, temos as classes Subsistema1, Subsistema2 e Subsistema3 que oferecem operações detalhadas. Também temos o Facade que trata-se de uma classe que oferece operações de alto nível que são selecionadas dos subsistemas. Por fim, temos uma classe Cliente que poderia ter um pacote próprio e tem como função realizar chamadas de operações de alto nível no Facade.

Existem alguns exemplos de implementação do padrão Façade no JDK como a classe java.util.Arrays, a classe java.io.InputStreamReader(InputStream) que retorna um Reader e a classe java.io.OutputStreamWriter(OutputStream) que retorna um Writer. Exemplos de implementação do padrão Facade no JEE temos o javax.servlet.http.HttpSession, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse que são subsistemas da classe javax.faces.context.ExternalContext que é o Facade propriamente dito que fornece facilidades como cookies, escopo de sessão e outras operações.

O padrão Facade nos oferece diversos benefícios como uma redução no acoplamento, pois o cliente não sabe nada sobre o subsistema, além de um aumento da capacidade de manutenção e capacidade de gerenciamento quando as mudanças são necessárias, reuso de funcionalidades porque isso encoraja o reuso de controles e de lógica de granularidade fina, consistência da execução dos serviços através da invocação do mesmo método consistentemente, redução na complexidade da lógica de negócio através do agrupamento de métodos relacionados e realizando a invocação através de um único método e, por fim, a centralização da segurança e gerenciamento do controle de transação.

Implementando um Façade em código puro

A implementação deste padrão de projeto é simples, pois ele não possui uma estrutura complicada ou um conjunto de regras. Para implementar um Facade basta possuirmos qualquer método que ofereça um acesso simples e fácil para um fluxo complicado.

Nos exemplos das Listagens 1 a 4 temos um Facade que abstrai um trabalho complicado já implementado para o usuário.

Listagem 1. Exemplo de uma classe UnidadeCentralDeProcessamento que processa um dado.

package com.devmediaexemplo.facade; class UnidadeCentralDeProcessamento { public void processaDado() { } }

Listagem 2. Exemplo de uma classe Memoria que carrega um dado.

package com.devmediaexemplo.facade; class Memoria { public void carregaDado() { } }

Listagem 3. Exemplo de uma classe DiscoRigido que faz a leitura de um dado.

package com.devmediaexemplo.facade; class DiscoRigido { public void leDado() { } }

Listagem 4. Exemplo de um Facade que abstrai uma lógica complexa.

package com.devmediaexemplo.facade; /* Facade */ class ComputadorFacade { private UnidadeCentralDeProcessamento unidadeCentralDeProcessamento; private Memoria memoria; private DiscoRigido discoRigido; public ComputadorFacade() { this.unidadeCentralDeProcessamento = new UnidadeCentralDeProcessamento(); this.memoria = new Memoria(); this.discoRigido = new DiscoRigido(); } public void run() { unidadeCentralDeProcessamento.processaDado(); memoria.carregaDado(); discoRigido.LeDado(); } }

Podemos verificar como o padrão Facade facilita a vida do usuário, visto que ele já se encarrega de realizar as instanciações e chamar os métodos necessários para realizarem o trabalho mais facilmente, sem precisar estender qualquer outra interface.

Para utilizarmos essa interface bastaria utilizar o código de exemplo da Listagem 5.

Listagem 5. Exemplo de como utilizar o Façade.

package com.devmediaexemplo.facade; class UsuarioExemplo { public static void main(String[] args) { ComputadorFacade facade = new ComputadorFacade(); facade.run(); } }

Um Façade é ainda mais útil quando possuímos diversas configurações padrão que deveriam ser feitas e um Facade abstrai essas configurações como no exemplo da Listagem 6.

Listagem 6. Exemplo de dois Facades que abstraem uma lógica complexa de uma máquina de lavar.

package com.devmediaexemplo.facade; public class MaquinaDeLavarRoupas { public void lavaRoupaMuitoSuja() { setTemperaturaAgua(100); setDuracaoCicloLavagem(90); setDuracaoCicloRotagem (10); addDetergente(); addAlvejante(); addAmaciante(); aquecerAgua(); iniciarLavagem(); } public void lavaRoupaPoucoSuja() { setTemperaturaAgua(40); setDuracaoCicloLavagem(20); setDuracaoCicloRotagem(10); addDetergente(); aquecerAgua(); iniciarLavagem(); } }

Para utilizarmos o tipo de lavagem “lavaRoupaPoucoSuja” basta executarmos o código da Listagem 7.

Listagem 7. Exemplo de como utilizar o Façade acima.

package com.devmediaexemplo.facade; // exemplo usando o façade class UsandoFacadeExemplo { public static void main(String[] args) { new MaquinaDeLavarRoupas().lavaRoupaPoucoSuja(); } }

No exemplo acima invocamos o método “lavaRoupaPoucoSuja” que fará toda a lógica complexa que está escondida pelo Facade.

Implementando um Façade na plataforma Java EE

Diferente de outros padrões o Java EE não oferece nenhuma implementação padrão já pronta do padrão de projeto Facade. No entanto, ele é simples e direto para ser implementado usando EJB stateful ou stateless. Usar Facade como sendo um EJB oferece a vantagem de acesso fácil para outro EJB que o Facade poderia requerer.

Para implementar o Façade como um Stateless Bean vamos supor que temos três EJBs distintos, mas todos possuindo funcionalidades relacionadas conforme os exemplos das Listagem 8 a 10.

Listagem 8. Exemplo de um EJB que retorna a identificação do usuário e verifica se o id corresponde ao cliente.

package com.devmediaexemplo.facade; import javax.ejb.Stateless; @Stateless public class ClienteService { public long getCliente(int sessionID) { // retorna o id do cliente logado no sistema return 100005L; } public boolean verificaId(long x) { // verifica se o id do cliente está correto return true; } }

Listagem 9. Exemplo de um EJB que retorna se o cliente pode realizar o empréstimo.

package com.devmediaexemplo.facade; import javax.ejb.Stateless; @Stateless public class EmprestimoService { public boolean avaliarCredito(long id, double quantidade) { // verifica se o cliente está qualificado para a quantidade solicitada return true; } }

Listagem 10. Exemplo de um EJB que retorna se o banco tem a quantia necessária e configura o novo saldo do cliente.

package com.devmediaexemplo.facade; import javax.ejb.Stateless; @Stateless public class ContaService { public boolean getEmprestimo(double quantidade) { // verificar se o banco tem o suficiente return true; } public boolean setSaldoCliente(long id, double quantidade) { // configure o novo saldo do cliente e retorna true se //tudo foi configurado corretamente return true; } }

Podemos agrupar esses serviços EJB em uma coleção lógica de funcionalidades relacionadas para formar uma implementação de um padrão de projeto Facade, conforme a Listagem 11.

Listagem 11. Exemplo de um Facade que encapsula e lógica para um empréstimo a um usuário.

package com.devmediaexemplo.facade; import javax.ejb.Stateless; import javax.inject.Inject; @Stateless public class ServicoDeBancoFacade { @Inject ClienteService clienteService; @Inject EmprestimoService emprestimoService; @Inject ContaService contaService; public boolean getEmprestimo(int sessionId, double quantidade) { boolean resultado = false; long id = clienteService.getCliente(sessionId); if(clienteService.verificaId(id)){ if(emprestimoService.avaliarCredito(id, quantidade)){ if(contaService.getEmprestimo(quantidade)){ resultado = contaService.setSaldoCliente(id, quantidade); } } } return resultado; } }

Dessa forma temos nosso Facade que encapsula sua própria lógica e fluxo, simplificando assim a hierarquia de chamadas dos métodos. Como regra geral é interessante possuirmos um Facade para cada subsistema e esses subsistemas comunicam-se uns com os outros através de Facades.

Se quisermos implementar o bean como um Stateful devemos apenas que adicionar a anotação @Stateful, isso marca o EJB como um bean Stateful.

O padrão de projeto Facade fornece uma interface unificada para um conjunto de interfaces em um subsistema. O Facade define uma interface de alto nível que facilita a utilização do subsistema. Portanto, o Facade simplifica uma interface sendo indicada ser utilizada em web services, onde um web service poderia fornecer acesso a um número pequeno de serviços que estariam por trás de um Facade. Devemos cuidar para não cometer alguns erros quando implementarmos o padrão Facade como: apenas criar uma nova camada como um Facade tende a adicionarmos mais classes no sistema, deve-se atentar que arquitetura em camadas é bom, mas devemos sempre avaliar a necessidade de realmente incluir uma nova camada no sistema; criar uma classe java e forçar a UI a interagir com outras camadas através dessa camada criada e simplesmente chama-la de Facade é um dos erros mais populares cometidos pelo desenvolvedores, devemos sempre saber que uma camada Facade não deve ser forçada e deve ser sempre opcional, caso o cliente desejar interagir com outros componentes diretamente ignorando assim o Façade isso deveria ser permitido; outro erro é um método na camada Facade ter apenas uma ou duas linhas que chamam outros componentes, se o Facade é tão simples não é necessário existir essa camada e os cliente podem acessar diretamente os componentes; diferente do que alguns acham um Facade não é um Controller; um Facade também não é uma camada que impõe segurança e esconde informações importantes e implementação; os subsistemas não devem conhecer o Facade e não deve possuir referências para Facades; não devemos criar Facades com antecedência, apenas se o subsistema se tornar complexo no futuro podemos realmente implementar um Facade.

Uma última observação é que devemos cuidar para não confundir o padrão Facade com outros padrões de projetos parecidos como o Adapter, Bridge e Decorator.

O padrão de projeto Adapter é utilizado quando necessitamos encaixar uma nova biblioteca, que pode ter sido adquirida de algum fornecedor, em um sistema já existente. Porém essa nova biblioteca de classes do fornecedor não pode ser alterada, assim criamos uma classe que faça uma adaptação da interface disponibilizada pelo fornecedor ao formato que o nosso sistema está esperando. A Bridge por sua vez é utilizada quando desejamos que uma interface possa variar independente das implementações. Um exemplo clássico da utilização da Bridge são os sistemas gráficos de janelas que precisam ser portáveis em diversas plataformas, assim teríamos uma Bridge para Windows, uma para Linux e uma para Mac, e então utilizaríamos cada um de acordo com a necessidade. Por fim, o padrão de projeto Decorator anexa responsabilidades adicionais a um objeto dinamicamente, os Decorator são uma alternativa flexível de subclasse para estender a funcionalidade.

Portanto, o adaptador é um intermediador que recebe solicitações do cliente e converte essas solicitações num formato que o fornecedor entenda, já um Bridge desacopla uma abstração da sua implementação, de modo que as duas possam variar independentemente, ou seja, uma Bridge fornece um nível de abstração maior que o Adapter, pois são separadas as implementações e as abstrações, permitindo que cada uma varie independentemente, diferente do Decorator que é um padrão utilizando quando se quer adicionar responsabilidade a objetos dinamicamente.

Para quem tiver mais interesse sobre o assunto quiser verificar uma aplicação construída na prática utilizando o padrão de projeto Facade na plataforma Java EE com framework JSF (Java Server Faces) disponibilizamos um artigo em [4] nas referências bibliográficas. Neste artigo os autores discutem como funciona a arquitetura geral de um sistema de controle acadêmico, como era antes o sistema implementado sem padrões de projeto e como ficou o sistema posteriormente utilizando o padrão de projeto Facade. Os autores também detalham exemplos práticos codificados do sistema desenvolvido.

Bibliografia

[1] Erich Gamma, Ricard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994).

[2] Java EE 6 and the Ewoks
http://www.devchronicles.com/2011/11/javaee6-and-ewoks.html.

[3] Eric Freeman, Elisabeth Robson, Bert Bates, Kathy Sierra. Head First Design Patterns. O'Reilly Media, 2004.

[4] Mateus Antonio Constanzo Silva e Ivan João Foschini. Implementação do padrão façade utilizando o Framework JavaServer Faces: um estudo de caso. ISSN 2316-2872 T.I.S. São Carlos, v. 1, n. 1, p. 20-27, jul. 2012.

Artigos relacionados