Implementando o Pattern Model View Controller na Plataforma Java EE

Veja neste artigo o que é o padrão de projeto Model View Controller e como podemos implementá-lo na nova plataforma Java EE 7 utilizando APIs que facilitaram essa tarefa.

O padrão de projeto MVC (Model View Controller) é um dos padrões de projeto mais presentes no desenvolvimento de aplicações atualmente. Conforme os autores do livro “Core J2EE Design Patterns”, se o processamento da informação da camada de apresentação não for encapsulado, tem-se uma probabilidade alta de ser criado um sistema com um alto acoplamento e difícil manutenção e extensão.

A separação de conceitos que o MVC fornece aos projetistas e desenvolvedores permite que as modificações realizadas tanto na camada de negócio quanto na interface com usuário sejam muito mais simples e independentes.

Os desenvolvedores de aplicações Web são os que mais utilizam e se beneficiam desse padrão, mas isso não significa que ele seja restrito apenas a esse tipo de aplicação.

No restante do artigo veremos o que é o MVC, como podemos implementá-lo como um POJO e como ele é implementado na nova plataforma Java EE 7.

Padrão MVC

Quando o padrão de projeto MVC está sendo estudado é interessante saber o que significa cada camada e o que ela contém.

Na camada Modelo tem-se as informações da aplicação e a lógica de negócio relacionadas. O Model (Modelo) pode ser representado por um objeto ou um grafo complexo de objetos relacionados. Nas aplicações Java EE, a informação é encapsulada nos objetos de domínio frequentemente implantados (deployed) no servidor de aplicação por meio de um módulo EJB.

As informações precisam ser transportadas entre as camadas e para isso utiliza-se o DTO (Data Transfer Object). Por exemplo, para transportar os dados a serem armazenados numa base de dados podemos utilizar um DTO contendo essas informações, então acessamos a camada de acesso ao banco de dados passando o DTO que contém os dados e só após isso os dados podem ser armazenados no banco de dados.

A camada View (Visualização) é a representação visual da informação contida na camada de Modelo. Um subconjunto do Modelo é representado em uma única View. Assim, ela é como um filtro para a informação contida no Modelo. Dessa forma, o usuário interage com a informação dele por meio de uma representação visual da View. Além disso, o usuário invoca a lógica de negócio que, por sua vez, age sobre os dados do Modelo.

A outra camada é o Controller (Controlador), que é responsável por ligar as duas camadas anteriores, ou seja, o Controller liga a View ao Modelo e direciona o fluxo da aplicação. Isto permite escolher qual View será exibida ao usuário em resposta à entrada e a lógica de negócio que será processada em cima dela. Assim, o Controller recebe uma mensagem da View, que por sua vez encaminha ao Model. Este prepara uma resposta e envia de volta ao Controller que escolhe a View adequada e a envia ao usuário.

O padrão MVC abrange o cliente e camada intermediária de uma arquitetura de múltiplas camadas. Em um ambiente Java EE, o Model é localizado na camada de negócio, normalmente na forma de um módulo EJB (Enterprise JavaBeans). Portanto, nas aplicações empresariais utilizamos um EJB para implementar o Model.

O Controller e a View estão localizados na camada Web. A View normalmente é construída utilizando JavaServer Faces (JSF), JavaServer Pages (JSP) ou utilizando frameworks como Primefaces.

O Controller normalmente é um Servlet que recebe requisições Hypertext Transfer Protocol (HTTP) do usuário.

Na próxima seção veremos mais detalhes de como implementar esse padrão junto ao JavaEE

Implementando o MVC

Existem diferentes formas para implementar o MVC, sendo as duas mais citadas na bibliografia atual e mais utilizada nos ambientes corporativos analisadas a seguir:

A principal diferença entre o Tipo I e o Tipo II é onde a lógica do Controller está localizada, sendo que no Tipo I ela está na View e no Tipo II ela está separada da View como num Servlet, ou seja, uma classe separada usada para estender a lógica.

O Tipo II do MVC é implementado por diversos frameworks como o Spring MVC, Struts, Grails e Wicket.

A partir da próxima seção veremos como implementar o Tipo I do MVC.

Implementando o padrão MVC em Código Puro (POJO)

O padrão MVC pode ser implementado utilizando as tecnologias web do Java, determinando onde redirecionar o usuário com base na requisição do usuário. Isso também ajuda a manter a única responsabilidade do Controller.

Primeiramente começamos definido-o, pois é no Controller que implementaremos a resposta a qualquer requisição HTTP GET feita no caminho /usuarios/*. O mapeamento deste relacionamento é definido no arquivo web.xml conforme mostrado na Listagem 1.

Listagem 1. Exemplo de implementação do web.xml definindo o Controller da aplicação.

<servlet> <servlet-name>FrontController</servlet-name> <servlet-class>br.com.devmedia.mvc.pojo.FrontController</servlet-class> </servlet> <servlet-mapping> <servlet-name>FrontController</servlet-name> <url-pattern>/usuarios/*</url-pattern> </servlet-mapping>

Nesse código mapeamos a classe Controller que está localizada em br.com.devmedia.mvc.pojo.FrontController para quaisquer requisições realizadas por meio da URL "/usuarios/*". Portanto, toda requisição feita para esta URL é redirecionada para a classe FrontController, que fará o processamento inicial da solicitação.

Como a plataforma Java EE 7 suporta a nova especificação Servlets 3.0, podemos simplesmente anotar a classe Controller como @WebServlet({"/usuarios/*"}), dispensando o uso do arquivo web.xml.

Quando essas solicitações forem realizadas, o método doGet() será invocado no Servlet. No exemplo da Listagem 2 temos uma implementação de exemplo do Controller. Neste código, o objeto Action é retornado do AbstractActionFactory, que determina a localização da View a ser retornada ao usuário.

Listagem 2. Exemplo de implementação de um Controller.

package br.com.devmedia.mvc.pojo; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FrontController extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Action action = AbstractActionFactory.getInstance().getAction(request); String view = action.execute(request, response); //chama a view passando como parâmetro a requisição recebida e um objeto de resposta getServletContext().getRequestDispatcher(view).forward(request,response); } }

No exemplo tem-se que o AbstractActionFactory cria uma instância da classe ActionFactory. O método getAction() aceita um objeto HttpServletRequest, que contém uma referência para o URI da localização solicitada, utilizando-a para determinar qual objeto Action retornar ao Controller.

O código da Listagem 3 mostra um exemplo da classe AbstractActionFactory e da classe ActionFactory. Pode-se verificar que há um mapeamento dos caminhos de requisições URI e dos objetos Action no Map action. Um objeto Action é escolhido do mapa baseado na solicitação da URI e retornado ao Controller.

Listagem 3. Exemplo de implementação das fábricas.

package br.com.devmedia.mvc.pojo; public class AbstractActionFactory { private final static ActionFactory actionFactory = new ActionFactory(); //Retorna uma instância única de ActionFactory public static ActionFactory getInstance() { return actionFactory; } } package br.com.devmedia.mvc.pojo; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; public class ActionFactory { private Map<String, Action> actions = new HashMap<>(); private Action action; public ActionFactory() { actions.put("GET/usuarios", new HomeAction()); actions.put("GET/usuarios/listausuarios", new ListaUsuariosAction()); } public synchronized Action getAction(HttpServletRequest request) { String path = request.getServletPath() + request.getPathInfo(); String actionKey = request.getMethod() + path; System.out.println(actionKey); action = actions.get(actionKey); if(action == null){ action = actions.get("GET/usuarios"); } return action; } }

A implementação concreta de Action deve fornecer uma implementação para o método execute(). Este é o responsável por executar a lógica de negócio necessária para gerar a página que o usuário requisitou. Entre outras coisas, ele pode realizar uma consulta à base de dados para obter a informação, executar cálculos ou gerar um arquivo.

No código da Listagem 4temos a implementação do método execute(), que constrói uma lista de usuários que são adicionados como um atributo para o objeto de requisição. Após isso, ele retorna a localização da View para ser exibida ao usuário. A informação agora armazenada no objeto request é acessada pela página listausuario.jsp e exibida.

Listagem 4. Exemplo de uma classe Action.

package br.com.devmedia.mvc.pojo; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ListaUsuariosAction implements Action { public String execute(HttpServletRequest request, HttpServletResponse response) { //Normalmente esses dados são buscados de um módulo EJB que busca às informações na base de dados List<String> listaUsuario = new ArrayList<>(); listaUsuario.add("Wander Wildner"); listaUsuario.add("Carlos Gerbase"); listaUsuario.add("Luciana Tomasi"); listaUsuario.add("Heron Heinz"); request.setAttribute("listausuarios", listaUsuario); return "/WEB-INF/pages/listausuarios.jsp"; } }

O objeto Action retorna para o Controller, que recebe a localização da página para a qual deverá enviar os objetos request e response, conforme mostra o exemplo a seguir e que já foi mostrado anteriormente no código do Controller:

String view = action.execute(request, response); getServletContext().getRequestDispatcher(view).forward(request, response);

No exemplo da Listagem 5 temos uma página JSP que acessa a variável requestScope e retorna a lista “listaUsuario” criada em ListUserAction. Após isso, realiza-se uma iteração sobre a coleção e exibe-se os nomes.

Listagem 5. Exemplo de implementação da página listausuario.jsp que foi solicitada pelo usuário.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix ="c" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Lista de Usuários</title> </head> <body> <h1 > Os usuários da nossa banda favorita são: </h1 > <c:forEach items="${requestScope.listausuarios}" var="usuario" > <br> $ </c:forEach > </body> </html>

Esse exemplo mostrou como pode-se criar uma implementação do padrão MVC. Na próxima seção será visto como implementar essa mesma aplicação usando as vantagens da plataforma Java EE 7.

Implementando o padrão MVC na Plataforma Java EE 7

Utilizando os pojos tínhamos que escrever a lógica do Controller, o mapeamento das URLs para classes Controller e escrever bastante código para a implementação completa do MVC. Porém, a plataforma Java EE simplificou bastante a implementação do padrão MVC. Assim, precisamos apenas concentrar os esforços na View e no Model, pois o FacesServlet cuida da implementação do Controller para os desenvolvedores. Nas próximas seções falaremos um pouco mais sobre o FacesServlet.

FacesServlet

O FacesServlet cuida do gerenciamento das requisições do usuário e entrega a View para o usuário, assim como o nosso Controller implementado anteriormente. Ele gerencia o ciclo de vida das aplicações que usam o JSF para construir a interface com o usuário. Todas as requisições do usuário passam pelos FacesServlet.

Agora vamos exemplificar como fazer um MVC utilizando FacesServlet e JSF. É importante ressaltar que a linguagem de declaração da view para JSF é chamada facelets. Os facelets são substitutos dos JSPs e são escritos em XHTML usando CSS (Cascading Style Sheets).

O JSF inclui conceitos de backing beans. Esses são POJOs (Plain Old Java Objects) anotados com @Named e @RequestScope. Anteriormente à plataforma Java EE 7 eles eram anotados com @ManagedBean, acessíveis por meio de uma página JSF enquanto durava a solicitação HTTP, porém o tipo do escopo pode ser alterado, mas isso é melhor estudado em artigos específicos para JSF. Podemos nos referir a esses métodos diretamente no JSF.

No código da Listagem 6 reescrevemos a classe ListaUsuariosAction utilizando o backing bean.

Listagem 6. Classe ListaUsuariosAction reescrita como um backing bean

package com.devchronicles.mvc.javaee; import java.util.ArrayList; import java.util.List; import javax.enterprise.context.RequestScoped; import javax.inject.Named; @RequestScoped @Named public class ListaUsuariosAction { private List<String> listaUsuario = new ArrayList<>(); public List<String> getListaUsuario() { return listaUsuario; } public String execute() { listaUsuario.add("Wander Wildner"); listaUsuario.add("Carlos Gerbase"); listaUsuario.add("Luciana Tomasi"); listaUsuario.add("Heron Heinz"); return "/WEB-INF/pages/listausuarios.xhtml"; } }

O próximo passo é criar um arquivo index.xhtml, que é o ponto inicial de contato do usuário com a nossa aplicação. Este arquivo substitui o home.jsp e é chamado diretamente pelo browser. A proposta deste JSF é chamar o método execute() em ListaUsuariosAction, que prepara a informação para a View listausuarios.xhtml.

O código da Listagem 7 mostra como este método é chamado.

Listagem 7. Exemplo de uma página para o padrão MVC.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Bem-vindo à aplicação de Exemplo devmedia</title> </h:head> <h:body> <h1>Interaja com a nossa aplicação abaixo</h1> <h:form> <h2>Clique aqui para ver uma <h:commandLink value="lista de usuários" action="#{listaUsuariosAction.execute}"/>.</h2> </h:form> </h:body> </html>

Usamos a tag h:commandLink e referenciamos o backing bean e o método execute() no elemento action. O método é chamado diretamente pelo JSF. Este gera a lista de usuários, retorna a localização da View que exibirá a lista e, por fim, invoca o método getListaUsuario(), exibindo a lista de usuários.

Segue o código da Listagem 8 que apresenta às informações retornadas ao usuário.

Listagem 8. View que exibe a informação do Model.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"> <head> <title>Lista de Usuários</title> </head> <body> <h1>Os usuários da nossa banda favorita são:</h1> <ui:repeat value="#{listaUsuariosAction.listaUsuario}" var="usuario"> <h:outputText value="#" /> <br/> </ui:repeat> </body> </html>

Quando realizamos o deploy da aplicação, a View index.xhtml exibe o link, que quando clicado exibe a lista de usuários como a seguir:

Os usuários da nossa banda favorita são: Wander Wildner Carlos Gerbase Luciana Tomasi Heron Heinz

Apenas isso já é necessário para construirmos uma aplicação utilizando a plataforma Java EE 7. Isso reduz bastante o esforço dos programadores em funcionalidades padrão que podem ser solucionadas por uma plataforma tão robusta quanto a Java EE 7, poupando assim o tempo e permitindo concentrar-se na lógica de negócio da aplicação.

Bibliografia

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

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

[3] Deepak, A.; Dan, M.; John, C.; Core J2EE Patterns: Best Practices and Design Strategies (2nd Edition). Prentice Hall, 2003.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados