Padrões de Projetos: Introdução aos Padrões Front Controller e Command

Veja neste artigo uma introdução ao padrão de projeto Front Controller, um dos padrões mais utilizados para projetos Web. Também analisaremos o padrão Command que está diretamente relacionado ao padrão Front Controller.

O Front Controller é implementado em diversos frameworks de ponta que são utilizados em diversos projetos de diferentes complexidades. Exemplo de dois frameworks que utilizam o padrão Front Controller é o Struts e o JSF, dois frameworks dos mais utilizados em projetos Java para aplicações que executam na Web. Entendendo o Front Controller e entendendo o funcionamento do padrão Command podemos entender muito melhor como ambos funcionam e como eles trabalham juntos para resolver problemas recorrentes nas aplicações Web.

No restante do artigo veremos como funcionam ambos os padrões, suas vantagens, desvantagens e como eles se relacionam no Front Controller. Também veremos um exemplo que demonstra a utilização dos dois padrões.

Nota: Confira nossos Cursos de Engenharia de Software.

Introduzindo o Padrão Front Controller

O Front Controller ou Controlador Frontal é um padrão arquitetural que se comporta como um controlador tratando todas as solicitações para um site Web e então roteia para uma ação (ou comando).

Segue na Figura 1 um diagrama de classes UML mostrando o relacionamento do Front Controller com outras partes do padrão.

Figura 1. Diagrama de classes do padrão arquitetural Front Controller

Sites relativamente complexos possuem funcionalidades que são similares entre si e que devem ser realizadas ao tratar solicitações como: checar a segurança, internacionalização, fornecer uma página em resposta, etc. Se muitos desses comportamentos forem espalhados para diversos objetos temos uma enorme quantidade de duplicação. Assim sendo, o objetivo principal do Front Controller é consolidar todo o tratamento de solicitações através de um único objeto que faz essa manipulação. Este objeto também pode ser alterado em tempo de execução através de decoradores.

No restante do artigo veremos melhor como é o funcionamento do padrão, quais são suas vantagens, desvantagens e um exemplo prático exemplificando o uso do padrão.

Funcionamento

Basicamente o Front Controller trata todas as chamadas vindas de um site web e é organizado em duas partes: através de um Manipulador Web e uma hierarquia de Comandos. O Manipulador Web é o objeto que efetivamente recebe as solicitações HTTP do tipo POST ou GET do servidor web. Ele extrai as informações necessárias da URL e das solicitações e então decide que tipo de ação iniciar e por fim delega a um objeto Comando para executar a ação. Vale salientar que tanto o Manipulador Web quanto o objeto Comando são partes do Front Controller. Dessa forma, o Comando escolhe qual Visão (ou página) usar para a resposta. O Manipulador Web tem como única responsabilidade escolher qual Comando executar.

O Manipulador Web é implementado como uma classe e não como uma página servidora visto que ele não produz nenhuma resposta. Os Comandos também são implementados como classes ao invés de páginas servidoras. O Manipulador Web é visto como algo simples já que a sua função é apenas decidir qual comando executar.

Segue na Figura 2 um diagrama de sequência que ajudar a entender um pouco como funciona a mecânica do padrão Front Controller.

Figura 2. Diagrama de Sequência para o padrão Front Controller

Como podemos observar na figura acima o Manipulador Web trata das solicitações, analisa a URL e pode decidir qual comando executar. O Manipulador Web pode decidir qual Comando utilizar tanto de forma estática quanto dinâmica. Na versão estática ele apenas analisa a URL e usa a lógica condicional. Na versão dinâmica ele pega um pedaço padrão da URL e usa a instanciação dinâmica para criar uma classe comando. A forma dinâmica será exemplificada mais adiante. Cada caso tem suas vantagens, na versão estática temos a vantagem da lógica explicita, verificação de erros no despacho em tempo de compilação e bastante flexibilidade na aparência das URLs. Na versão dinâmica podemos adicionar novos comandos sem alterar o Manipulador Web.

Vantagens

Utilizando o Front Controller devemos torna-lo único no servidor web, e por ser único precisa ser configurado apenas uma única vez. Com objetos Comando dinâmicos podemos adicionar novos Comandos sem alterar nada, o que também torna mais fácil portar a aplicação. Outra vantagem interessante é que como o nosso objeto Comando é criado a cada solicitação não precisamos nos preocupar com a concorrência.

Apesar de possuirmos apenas um controlador podemos usar decoradores para melhorar o comportamento do controlador em tempo de execução criando decoradores para autenticação, codificação de caracteres, internacionalização, etc.

Desvantagens

O Front Controller precisa ser configurado como único no servidor Web e a sua configuração pode ser complicada dependendo do servidor. Também devemos ter cuidado para não compartilhar objetos nos Comandos ou então a vantagem de não se preocupar com a concorrência pode se tornar um problema.

Exemplo

No exemplo abaixo criaremos um projeto em Java utilizando Servlets para exemplificar o uso do padrão Front Controller para um projeto Web.

A aplicação é relativamente simples, mas demonstra claramente o objetivo do padrão e como ele pode ser estruturado através de um controlador e objetos de comando associados. Basicamente temos uma aplicação que tem uma página inicial contendo um link para noticias e informações de um clube de futebol (grêmio) e outro link para informações de outro clube de futebol (internacional). Dependendo do link clicado a aplicação recebe a URL e desmembra essa URL em busca do Comando a ser chamado. Esse Comando terá a responsabilidade de repassar para a tela do usuário a página correta para a solicitação do usuário.

Primeiramente criamos um projeto que está organizado conforme a Figura 3. Podemos verificar dois pacotes: “commands” que possui os comandos chamados pelo Controller e o pacote “servlets” que possui o Controller responsável por receber as solicitações vindas da Web.

Figura 3. Organização do Projeto

Segue na Listagem 1 o código da classe Controller.

package servlets; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import commands.Command; public class Controller extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Executa o Comando conforme a URL Command comando = null; try { comando = (Command)Class.forName("commands."+request .getParameter("command")).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } comando.execute(request, response); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } }
Listagem 1. Exemplo do código-fonte da classe Controller

Podemos verificar no código acima que os métodos doGet() e doPost() recebem solicitações HTTP do tipo GET ou POST. Ambos delegam para o método processRequest() que pega o parâmetro “command” da URL e associa com uma classe Command. Para exemplificar podemos considerar a URL abaixo:

http://localhost:8080/ProjetoFrontController/Controller?command=ExibePaginaGremistas

O nosso Controlador pega o que foi passado como valor no parâmetro “command” (o valor está depois do igual), ou seja, “ExibePaginaGremistas”, e chama a classe Comando que possui este nome. Por sua vez o Comando é responsável por direcionar o usuário para uma página especifica. O Comando também poderia fazer diversas outras coisas mais especificas, mas nesse caso para exemplificar ele apenas chama a página de exibição adequada para a solicitação do usuário. Segue nas Listagens 2, 3 e 4 um exemplo da interface Command e das classes concretas ExibePaginaGremistas e ExibePaginaColorados.

package commands; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface Command { public void execute(HttpServletRequest request, HttpServletResponse response); }
Listagem 2. Exemplo do código-fonte da Interface Command
package commands; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ExibePaginaColorados implements Command { public void execute(HttpServletRequest request, HttpServletResponse response) { try { RequestDispatcher d = request.getRequestDispatcher("/colorados.jsp"); d.forward(request,response); } catch (IOException | ServletException e) { e.printStackTrace(); } } }
Listagem 3. Exemplo do código-fonte da classe ExibePaginaColorados
package commands; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ExibePaginaGremistas implements Command { public void execute(HttpServletRequest request, HttpServletResponse response) { try { RequestDispatcher d = request.getRequestDispatcher("/gremistas.jsp"); d.forward(request,response); } catch (IOException | ServletException e) { e.printStackTrace(); } } }
Listagem 4. Exemplo do código-fonte da classe ExibePaginaGremistas

Ambas as classes concretas redirecionam para as respectivas páginas “gremistas.jsp” ou “colorados.jsp”.

Segue nas Listagens 5, 6 e 7 como ficaram as JSPs “index.jsp”, “gremistas.jsp” e “colorados.jsp”.

<html> <body> <a href="/ProjetoFrontController/Controller? command=ExibePaginaGremistas">Exibe Página para Gremistas</a> <br/> <a href="/ProjetoFrontController/Controller? command=ExibePaginaColorados">Exibe Página para Colorados</a> </body> </html>
Listagem 5. Exemplo do código-fonte da página index.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <style type="text/css"> .odd{ background-color:blue} .even{background-color:white} </style> </head> <body> Olá Gremistas, confira tudo sobre o grêmio aqui. <br/> <br/> <table border="1"> <thead> <tr> <th>Notícia</th> <th>Link</th> </tr> </thead> <tbody> <c:forEach var="i" begin="1" end="5" step="1" varStatus ="row"> <c:choose> <c:when test="${row.count % 2 == 0}"> <c:set var="estiloLinha" value="odd" /> </c:when> <c:otherwise> <c:set var="estiloLinha" value="even" /> </c:otherwise> </c:choose> <tr class="$"> <td> <c:out value="Notícia número $" /> </td> <td> <c:out value="Em breve! Aguarde." /> </td> </tr> </c:forEach> </tbody> </table> </body> </html>
Listagem 6. Exemplo do código-fonte da página gremistas.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <style type="text/css"> .odd{ background-color:red} .even{background-color:white} </style> </head> <body> Olá Colorados, confira tudo sobre o inter aqui. <br/> <br/> <table border="1"> <thead> <tr> <th>Notícia</th> <th>Link</th> </tr> </thead> <tbody> <c:forEach var="i" begin="1" end="5" step="1" varStatus ="row"> <c:choose> <c:when test="${row.count % 2 == 0}"> <c:set var="estiloLinha" value="odd" /> </c:when> <c:otherwise> <c:set var="estiloLinha" value="even" /> </c:otherwise> </c:choose> <tr class="$"> <td> <c:out value="Notícia número $" /> </td> <td> <c:out value="Em breve! Aguarde." /> </td> </tr> </c:forEach> </tbody> </table> </body> </html>
Listagem 7. Exemplo do código-fonte da página colorados.jsp

Para executar este código precisamos também configurar o arquivo web.xml para que o nosso Controller seja achado pelo framework. Segue o código da Listagem 8.

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Exemplo FrontController</display-name> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>frontcontroller</servlet-name> <servlet-class>servlets.Controller</servlet-class> </servlet> <servlet-mapping> <servlet-name>frontcontroller</servlet-name> <url-pattern>/Controller</url-pattern> </servlet-mapping> </web-app>
Listagem 8. Exemplo do código-fonte do arquivo de configuração web.xml

Quando o usuário visitar a página index.jsp ele possui duas opções de visualização, uma página com noticias do grêmio e uma página com noticias do internacional. Dependendo de qual tipo de noticias ele queira visualizar o Controller analisa o link clicado e chama o Comando correspondente que será responsável por direcionar o usuário para a página solicitada. Dessa forma, temos a exemplificação de como funciona o padrão arquitetural Front Controller. Existem diversos frameworks que implementam este padrão, o mais conhecido é o Struts através da ActionServlet e o JSF através do FacesServlet que é responsável por receber requisições HTTP e processá-las.

Introduzindo o Padrão Command

O Padrão de projeto Command encapsula uma solicitação como um objeto, o que lhe permite parametrizar outros objetos com diferentes solicitações, enfileirar ou registrar solicitações e implementar recursos de cancelamento de operações.

Dessa forma o padrão Command encapsula uma solicitação vinculando um conjunto de ações em um receptor especifico. Para fazer isto ele precisa empacotar as ações e o receptor em um objeto que expõe um único método normalmente chamado execute(). Quando execute() é chamado ele invoca as ações no receptor. Externamente nenhum outro objeto sabe o que realmente será invocado ou mesmo em qual receptor.

Segue na Figura 4 um diagrama de classes UML mostrando o relacionamento do Command com outras peças.

Command ">
Figura 4. Diagrama de classes do padrão Command

No diagrama acima o Cliente será responsável por criar um CommandConcreto e também deverá definir o seu Receptor. O Invocador possui um Command e em determinado momento pede para que o Command atenda a uma solicitação chamando o seu método execute(). A interface Command serve como interface para todos os Comandos concretos. O CommandConcreto implementa a interface Command e define um vinculo entre uma ação e um Receptor. Basicamente um Invocador faz uma solicitação chamando execute() e o CommandConcreto executa essa solicitação chamando uma ou mais ações no Receptor. O Receptor por sua vez sabe como executar as tarefas necessárias para atender a solicitação.

Funcionamento

O padrão Command estabelece um conjunto de interfaces comum e permite desconectar o autor de uma solicitação do objeto que deverá executar a ação solicitada. Essa desconexão é feita utilizando “objetos de Comando” que encapsula a solicitação para fazer algo em um objeto específico. Assim sendo, o solicitante não faz a menor ideia de como o Receptor fará a tarefa, ele apenas sabe que precisa chamar um Command e solicitar pelo seu método execute() que se encarregará de chamar o Receptor que realizará todo o trabalho necessário.

Segue na Figura 5 um diagrama de sequência que ajuda a entender um pouco como funciona a mecânica desse padrão.

Figura 5. Diagrama de Sequência para o padrão Command

Podemos verificar no diagrama de sequência acima que um Cliente é responsável por criar o Invocador, o Comando e o Receptor. Após isso, o Cliente seta o comando no Invocador e solicita para que o Invocador faça alguma operação. O Invocador será responsável por chamar o método execute() do Comando que por sua vez fará todo o trabalho necessário para executar uma determinada operação. Para realizar outra operação, bastaria criar um novo Comando, configura-lo no Invocador e chamar a operação do Invocador. Se quisermos criar um novo Receptor precisaríamos apenas criar um novo Comando para esse novo Receptor, e deixar para que o Cliente crie este Comando, sete e faça a operação normal. Ou seja, para criar novos Receptores e Comandos não mexemos uma linha no código, apenas criamos mais artefatos que farão parte da evolução do projeto.

Vantagens

Como o padrão Command estabelece um conjunto comum de interfaces, isso acaba facilitando a inclusão de novas classes no futuro, visto que não há modificações no código. Outra vantagem é que o padrão evita precisarmos conhecer os detalhes específicos de uma classe Receptora, bastando que um determinado Comando tenha esse conhecimento o que torna o código mais simples e desacoplado evitando muitos “if’s” espalhados ou amontoados no código.

Os Comandos também podem se tornar mais complexos através de MacroComandos que permitem a chamada simultânea de múltiplos Comandos e permite reversão de Comandos através da implementação de um método undo(). Comandos também podem ser utilizados para implementar registros de ações executadas (logging) e sistemas transacionais.

Desvantagens

O padrão Command pode ser mal entendido ou demorar até ser compreendido por programadores inexperientes ou que não conhecem padrões de projeto. Isso pode acarretar em falhas ou desestruturação no código.

Referências Bibliográficas:
  • F. Martin. Padrões de Arquitetura de Aplicações Corporativas. Bookman, 2008.
  • Freeman E.; Head First Design Patterns, Segunda Edição. O’Reilly, 2009.

Artigos relacionados