Preview only show first 10 pages with watermark. For full document please download

Easy-java 056 Zxcderty

Descrição: java

   EMBED


Share

Transcript

Assine agora e tenha acesso a todo o conteúdo da DevMedia: www.devmedia.com.br/mvp Edição 56 • 2015 • ISSN 2173625-4 EXPEDIENTE Editor Eduardo Spínola ( [email protected]) Atendimento ao leitor A DevMedia possui uma Central de Atendimento on-line, onde você pode tirar suas dúvidas sobre serviços, enviar críticas e sugestões e falar com um de nossos atendentes. Através da nossa central também é possível alterar dados cadastrais, consultar o status de assinaturas e conferir a data de envio de suas revistas. Acesse www.devmedia. com.br/central,, ou se preferir entre em contato conosco através do com.br/central Fale com o Editor! É muito importante para a equipe saber o que você está achando da revista: que tipo de artigo você gostaria de ler, que artigo você mais gostou e qual artigo você menos gostou. Fique a vontade para entrar em contato com os editores e dar a sua sugestão! Se você estiver interessado em publicar um artigo na revista ou no site Easy Java Magazine, entre em contato com o editor,informando o título e mini-resumo do tema que você gostaria de publicar: telefone 21 3382-5038. Consultor Técnico Diogo Souza (diogosouzac@gma ([email protected]) il.com) Produção Publicidade  [email protected]   – 21 3382-5038  Jornalista Responsável Kaline Dolabella - JP24185 Anúncios– Anunciando – Anunciando nas publicações e nos sites do Grupo DevMedia, Capa e Diagramação Romulo Araujo você divulga sua marca ou produto para mais de 100 mil desenvolvedores de todo o Brasil, em mais de 200 cidades. Solicite nossos Media Kits, com EDUARDO OLIVEIRA SPÍNOLA eduspinola.wordpress.com @eduspinola / @Java_Magazine detalhes sobre preços e formatos de anúncios. Sumário Conteúdo sobre Boas Práticas, Artigo no estilo Solução Completa 04 - UML e Java na prática [ John Soldera ] Conteúdo sobre Boas Práticas, Artigo no estilo Solução Completa 13 - Refatorando código com padrões de projeto [ Gabriel Feitosa ] Conteúdo sobre Boas Práticas, Artigo no estilo Solução Completa 21 - Aprenda a utilizar o Spring Web Flow [ Alessandro Jatobá ] UML e Java na prática UML e Java na prática Aprenda nesse artigo o que é UML e como desenvolver uma aplicação simples a partir dos diagramas de casos de uso e de classes A linguagem de modelagem UML ( Unified Modeling Language) é uma das linguagens de modelagem de software mais utilizadas no mundo. Como um dos seus diferenciais, oferece um conjunto de diagramas padronizados para modelar as mais variadas características dos sistemas de informação, como o diagrama de casos de uso, diagrama de classes, diagrama de sequência, entre outros. Através do diagrama de casos de uso é possível modelar as funcionalidades que o sistema fornecerá ao usuário; com o diagrama de classes, pode-se definir as entidades e seus relacionamentos; e através do diagrama de sequência, conseguimos informar a ordem em que os objetos se comunicam para realizar as operações descritas nos casos de uso. Pelo fato da linguagem Java ser orientada a objetos, a conversão dos diagramas da UML para código-fonte é simples e direta. Além disso, essa linguagem apresenta uma sintaxe simplificada quando comparada com outras opções, o que facilita o processo de desenvolvimento de software. Dito isso, com o intuito de ilustrar o processo de codificação a partir de diagramas UML, uma aplicação  básica de controle de estoque será desenvolvida de forma que o código-fonte seja organizado em camadas, solução adotada para a construção da maioria dos sistemas hoje em dia. Lembre-se q ue uma abordagem simplificada para organizar o código-fonte de um programa é dividir a aplicação em três níveis: camada de apresentação, camada de negócios e camada de dados. Como ocorre normalmente em qualquer projeto de software, o ciclo de desenvolvimento envolve quatro grandes etapas: análise, projeto, implementação e testes. Tidas como fundamentais, pular uma dessas etapas pode acarretar imprecisões, retrabalho e prejudicar diretamente a qualidade do produto. É durante a análise que o analista investiga e descobre como funciona o sistema em questão. É nessa fase que o problema do mundo real é estudado e a solução proposta é descrita através de modelos que sejam capazes de guiar o projeto do software. 4 Fique por dentro Através de exemplos este artigo abordará como converter alguns dos principais diagramas da linguagem de modelage m UML, obtidos durante o projeto de software, em código Java. Ao longo desse estudo será analisada a documentação em UML de uma aplicação de controle de estoque, mais especificamente os diagramas de casos de uso e de classes, e os mesmos serão convertidos, passo a passo, para a criação de classes, atributos e métodos. O conhecimento sobre estes conceitos é algo fundamental à caixa de ferramentas de todo desenvolvedor que tem como meta códigos mais fáceis de compreender, evoluir e manter. A linguagem UML também atua na etapa de projeto do software, momento em que são definidos a arquitetura do sistema, a linguagem de programação utilizada, o banco de dados, a tecnologia de interface gráfica e a adoção de padrões de projeto. Em seguida, na fase de implementação, o projeto de software é interpretado e transformado em código na linguagem escolhida usando os padrões especificados. Por último, ocorre a fase de teste, onde cada funcionalidade do sistema é testada. Saiba que a fase de teste pode ser executada desde o início do projeto, através da criação de casos de testes integrados ao código-fonte, com o auxílio de ferramentas como o JUnit. Com base no conteúdo exposto, a seguir será apresentado o desenvolvimento de uma aplicação exemplo a partir de alguns diagramas da linguagem UML, artefatos estes que visam especificar características importantes do software durante a fase de análise. Aplicação exemplo A aplicação exemplo desse artigo tem o objetivo de realizar o controle de estoque de um supermercado. Com isso em mente, vamos modelar esta solução utilizando dois dos diagramas mais conhecidos da UML, o diagrama de casos de uso e o diagrama de classes. Através deles é possível especificar o que usuário pode fazer no sistema, bem como as entidades do contexto do problema; neste caso, fornecedores, produtos, unidades de medida e pedidos. Porém, para não tornar o artigo muito longo, vamos focar apenas na implementação de algumas funcionalidades: o cadastro de fornecedores, unidades, produtos e pedidos. Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Espera-se que com o conteúdo o exposto o leitor consiga desenvolver as demais opções. Casos de Uso Este diagrama descreve o que o usuário pode fazer. Como exemplo, vejamos, na Figura 1 , o primeiro caso de uso da aplicação em desenvolvimento: Cadastro de Fornecedor. Note que o usuário deve ter acesso a quatro funcionalidades relacionadas à entidade Fornecedor: criação, edição, exclusão e listagem. A Figura 2 apresenta outro caso de uso, de nome Cadastro de Unidade. Nele, de modo semelhante ao exemplo anterior, são descritas as iterações que o usuário do sistema terá com a entidade “unidade”. Assim, deve ser possível criar, editar, excluir e consultar unidades de medida, que serão usadas no cadastro de produtos – como será mostrado mais à frente. Encerrando a modelagem dos casos de uso, especificamos ainda os diagramas Cadastro de Produto e Cadastro de Pedido (vide Figuras 3 e 4), que, como verificado, seguem a mesma tendência apresentada pelos outros exemplos. Note que através dos casos de uso já estabelecemos uma divisão para o sistema, de forma que o usuário possa modificar cada parte do sistema separadamente. Com o auxílio destes, os diagramas subsequentes na análise consideram as informações levantadas e modelam outras características, como veremos a seguir. Diagrama de Classes É através da criação de classes que a informação gerenciada pelo sistema é organizada em entidades separadas. Sabendo disso, para que o software tenha um código com boa legibilidade e fácil manutenção, cada entidade deve conter apenas os atributos e métodos a ela relacionados. A partir disso, para estabelecer o funcionamento do sistema e também para compor entidades mais complexas, são definidos os relacionamentos entre essas classes. O diagrama de classes se encaixa exatamente nesse contexto e nada mais é do que uma forma de representar as classes/interfaces, atributos, métodos e relacionamentos de um sistema de forma visual. A partir dele o desenvolvedor terá um “mapa” do que implementar, o que facilita o trabalho de implementação e reduz o risco de criar códigos desnecessários. A Figura 5 apresenta o diagrama de classes da aplicação exemplo. Note que organizamos os dados da aplicação em cinco entidades: Fornecedor , Unidade , Pedido , ItemPedido e Produto. Além disso, especificamos seus atributos, métodos e relacionamentos. Ao analisar a entidade Fornecedor, por exemplo, verificamos que ele tem os atributos codigoFornecedor , endereço , telefone e nome. Como métodos, foram especificados o criarFornecedor() , editarFornecedor() , excluirFornecedor() , consultarFornecedor() e consultarFornecedores(). Ademais, note que ele se relacionada com a entidade Pedido. Com esta ligação estamos indicando que um fornecedor pode ter zero ou muitos pedidos. Esta mesma lógica se aplica às demais entidades, respeitando sempre as diferenças de cada tipo de relacionamento. Figura 1. Caso de Uso Cadastro de Fornecedor Figura 2. Caso de Uso Cadastro de Unidade Figura 3. Caso de Uso Cadastro de Produto Figura 4. Caso de Uso Cadastro de Pedidos Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 5 UML e Java na prática Fase de Projeto de Software Concluída a fase de análise, deve-se prosseguir para a fase de projeto de software, onde os diagramas obtidos anteriormente são convertidos em documentos ou outros diagramas de forma que seja mais fácil ao programador se basear neles para realizar o desenvolvimento do software. Durante a fase de projeto são produzidos documentos e diagramas semelhantes aos da fase de análise, porém tal informação sofre um detalhamento maior e os processos são expandidos, muitas vezes empregando-se de pseudocódigo para descrever procedimentos. Além disso, é na fase de projeto que são especificados os design patterns para facilitar e organizar a implementação do software, pois existe uma tendência das classes originais conterem muita informação pertencente a diferentes camadas da aplicação, o que causa uma forte vinculação entre essas camadas e prejudica diretamente a manutenção e legibilidade do código. Hoje já existem diversas formas de se organizar um software em camadas. Dentre elas, uma das mais conhecidas é o padrão  Model-View-Controller , que é amplamente empregado em Java para organizar o software em camada de apresentação, de modelo e de controle. Além deste, outro padrão bastante adotado é o Data Access Ob ject , opção que leva a uma divisão das classes em uma estrutu ra que abstrai e reduz o acoplamento do código com as tecnologias utilizadas para acesso a dados. Em nosso exemplo, cada classe presente na Figura 5 será refatorada em duas: uma classe DAO, que contém os métodos de acesso a dados, e uma classe DTO, que contém os atributos de cada entidade. Em definição simples, um objeto DTO representa um objeto de dados que pode ser enviado entre camadas distintas do software. Criação do Banco de Dados Uma vez que se tenha evoluído na especificação do diagrama de classes da fase de projeto, pode-se partir para a criação das tabelas no banco de dados, elementos que serão responsáveis pela persistência dos objetos que forem criados durante o ciclo de vida do software. Em muitos projetos, uma das primeiras coisas a serem definidas é o banco de dados. Nestes casos, comumente, um modelo ER (Entidade Relacionamento) pode ser a única documentação disponível no sistema. Entretanto, essa estratégia não é adequada, uma vez que antecipa e distorce o processo de desenvolvimento de software, além de centralizar todo o conhecimento do sistema sobre as poucas pessoas envolvidas no processo de análise. Outro aspecto importante em um projeto de software eficiente e robusto é criar no banco de dados todas as verificações necessárias sobre os dados para evitar o estado inconsistente da base, o que pode acontecer devido ao mau uso do sistema por parte do usuário ou em decorrência de falhas no software, como bugs ou falta de validações de dados de formulários. Portanto, é importante criar corretamente as chaves primárias, chaves estrangeiras e chaves ún icas (campos que não se repetem) a fim de barrar na camada de persistência a repercussão de eventuais falhas que possam ocorrer nas outras camadas. Figura 5. Diagrama de Classes 6 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Como parte da fase de projeto, os analistas geram a especif icação de cada tabela e de cada campo do banco de dados informando o tipo e o tamanho do mesmo. Além disso, ele mesmo pode criar a  base de dados se tiver conhecimento na área (e se a empresa não contar com DBAs em seu time) ou delegar a atividade a um administrador de banco de dados (DBA), que se encarrega de escrever os scripts de criação do banco e fazer as alterações. O script para criação do banco de dados da aplicação exemplo é mostrado na Listagem 1 e tem o objetivo de criar a base de dados, isto é, as tabelas e seus atributos, em um banco de dados MySQL, já considerando a especificação de chaves primárias, estrangeiras e únicas. Listagem 1 . Script para criação do banco de dados da aplicação exemplo. create database Estoque; create table Fornecedor ( codigoFornecedor int, endereco varchar(100), telefone varchar(15), nome varchar(100), PRIMARY KEY (codigoFornecedor), UNIQUE KEY (nome) ); Como visto nesse código, o nome da base de dados exemplo será “Estoque”. Observe também que foram definidos os tamanhos dos campos e seus tipos usando os tipos de dados disponíveis na especificação do MySQL. Além disso, através das chaves estrangeiras (FOREIGN KEY ) configuramos os relacionamentos entre as tabelas. Camada de Persistência Iniciaremos a implementação do nosso diagrama criando a camada de persistência. Para isso, com o intuito de manter uma boa organização do código, crie o pacote database , local onde as classes relacionadas a este requisito devem ser implementadas. Na Listagem 2  é apresentada a primeira classe da camada de persistência, DatabaseManager , a qual tem a função de estabelecer a conexão com o banco de dados e executar algumas consultas SQL. Listagem 2 . Código da classe DatabaseManager. 01. package database; 02. import java.sql.*; 03. create table Unidade( codigoUnidade int, sigla varchar(10),   descricao varchar(100), PRIMARY KEY (codigoUnidade), UNIQUE KEY (sigla) ); 04. public class DatabaseManager { create table Produto( codigoProduto int,   nomeProduto varchar(15), codigoBarras varchar(20), estoque float, descricao varchar(100), valorProduto float, codigoUnidade int, PRIMARY KEY (codigoProduto), FOREIGN KEY (codigoUnidade) REFERENCES Unidade (codigoUnidade), UNIQUE KEY (nomeProduto), UNIQUE KEY (descricao) ); 11. create table Pedido( codigoPedido int, dataPedido date, valorPedido float, codigoFornecedor int, PRIMARY KEY (codigoPedido), FOREIGN KEY (codigoFornecedor) REFERENCES Fornecedor (codigoFornecedor) ); 23. 30.  public PreparedStatement prepararStatament(String sql) throws SQLException { create table ItemPedido( codigoItem int, codigoPedido int, codigoProduto int, quantidade float, valorItem float, PRIMARY KEY (codigoItem), FOREIGN KEY (codigoPedido) REFERENCES Pedido (codigoPedido), FOREIGN KEY (codigoProduto) REFERENCES Produto (codigoProduto) ); 31. PreparedStatement statement; statement = connection.prepareStatement(sql); return statement; 05. 07. private static final String  JDBC_DRIVER = “com.mysql.jdbc.Driver”; private static final String DB_URL = “jdbc:mysql://localhost/Estoque”; 08.  private static final String USER = “root”; 06. 09.  private static final String PASSWORD = “password”; 10. private Connection connection; 12. public DatabaseManager() throws SQLException, ClassNotFoundException { 13. Class.forName( JDBC_DRIVER); connection = DriverManager.getConnection(DB_URL, USER, PASSWORD); 14. 15. } 16. 17.  public void executeSQL(String sql) throws SQLException { 18. Statement statement = null; statement = connection.createStatement(); statement.execute(sql); statement.close(); } 19. 20. 21. 22. 24.  25. public  ResultSet executeQuery(String sql) throws SQLException { Statement statement = null; 26. statement = connection.createStatement(); 27.  return statement.executeQuery(sql); 28. } 29. 32. 33.  34. } 35. 36. public void setAutoCommit( boolean autocommit) throws SQLException { connection.setAutoCommit(autocommit); 37. 38. } 39. } Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 7 UML e Java na prática Para que DatabaseManager funcione corretamente, é necessário ter o banco de dados MySQL instalado e em execução com a base de dados descrita na Listagem 1. A partir disso, para que a aplicação consiga se conectar à base, deve-se adicionar o conector de acesso ao  banco no classpath da aplicação. Este conector, também conhecido como driver JDBC, pode ser baixado do site do MySQL. Na Listagem 2 , o driver de acesso é carregado na linha 6. Na linha 7, é declarada a URL de acesso ao banco (repare no sufixo o nome do banco), e nas linhas 8 e 9 são informados o usuário e a senha da base. Nesta classe também foram implementados os métodos executeSQL() e executeQuery() , responsáveis por executar diretamente qualquer comando SQL (vide linhas 17 a 28), e o método prepararStatement() , criado para que seja possível preparar comandos SQL que são executados com frequência mais alta e assim garanti r um melhor desempenho (vide linhas 30 a 34). Por fim, tem-se o método setAutoCommit() , que permite criar transações, ou seja, executar conjuntos de comandos SQL de forma atômica. Para tanto, basta iniciar a transação com setAutoCommit(false) , o que faz com que os próximos comandos SQL enviados através da conexão com o banco de dados não sejam executados prontamente, e terminar com setAutoCommit(true) , o que efetiva todos os SQLs pendentes na conexão. Visto que optamos por usar PreparedStatements para otimizar a eficiência da aplicação e do banco de dados, deixaremos os scripts SQL de criação, edição, exclusão e consulta de registros de cada entidade em uma classe específica. O primeiro exemplo disso pode ser verificado na Listagem 3. O código-fonte apresentado nessa listagem contém os comandos SQL voltados para as operações básicas de cadastro de fornecedores a serem usados em PreparedStatements. Note que para especificar os parâmetros de cada comando são utilizadas interrogações, como demonstram as instruções de insert (linha 5), update (linha 9), delete (linha 16) e select (linhas 21). Observe ainda que existem dois comandos select: um para consultar um fornecedor pelo código (linha 21) e outro para consultar todos os fornecedores (linha 19). Uma melhoria que pode ser realizada ao esquema apresentado é adicionar o comando LIMIT nos SQLs de consulta, a fim de evitar o alto uso de memória no caso de as tabelas conterem um número muito grande de registros. De forma semelhante, na Listagem 4 são apresentados os comandos SQL para o cadastro de u nidades. Como o padrão de consulta é o mesmo, não é necessário explicar o seu código. O mesmo vale para as classes que implementam as operações relacionadas a produtos, pedidos e itens pedidos. O código-fonte destas pode ser verificado no arquivo disponibilizado para download nesta edição. Camada de Negócio Concluída a camada de persistência, pode-se partir para a camada de negócios, local onde devemos implementar, dentre outras coisas, as regras de negócio da aplicação. 8 Listagem 3 . Código da classe FornecedorSQL. 01. package database; 02. 03. public class FornecedorSQL { 04. 05.  public static String insert  = “insert into Fornecedor “ + “(codigoFornecedor, endereco, telefone, nome) “ 06. + “ values (?,?,?,?)”; 07. 08. 09.  public static String update = “UPDATE Fornecedor SET “ + 10. “codigoFornecedor = ?,” + 11. “endereco = ?,” + “telefone = ?, “ + 12. “nome = ? “ + 13. “WHERE codigoFornecedor = ?”; 14. 15. 16.  public static String delete = “delete from Fornecedor “ 17. + “where codigoFornecedor = ?”; 18. 19.  public static String selectAll  = “select * from Fornecedor”; 20. 21.  public static String select  = “select * from Fornecedor “ + “where codigoFornecedor = ?”; 22. 23. } Listagem 4 . Código da classe UnidadeSQL. 01. package database; 02. 03. public class UnidadeSQL { 04. 05.  public static String insert  = “insert into Unidade “ 06. + “(codigoUnidade, sigla, descricao) “ 07. + “ values (?,?,?)”; 08. 09.  public static String update = “UPDATE Unidade SET “ + “codigoUnidade = ?,” + 10. “sigla = ?,” + 11. “descricao = ? “ + 12. 13. “WHERE codigoUnidade = ?”; 14. 15.  public static String delete = “delete from Unidade “ + “where codigoUnidade = ?”; 16. 17. 18.  public static String selectAll  = “select * from Unidade”; 19. 20.  public static String select  = “select * from Unidade “ + “where codigoUnidade = ?”; 21. 22. } De acordo com os padrões selecionados na fase de projeto, esta camada será dividida em DTOs e DAOs, artefatos que correspondem a objetos de transmissão de dados e objetos de acesso a dados, respectivamente. Comecemos a implementação, então, pela classe FornecedorDto , que é responsável por conter todas as regras de negócio relacionadas ao cadastro de fornecedores. O seu código é apresentado na Listagem 5 e para manter as boas práticas da organização de pacotes, crie-a dentre do pacote negocio.dto. Como verificado, essa classe deve ser implementada de forma que cada objeto contenha todos os dados relativos a um fornecedor, o que envolve os atributos codigoFornecedor ,  endereco , telefone e nome (vide linhas 4 a 7). Essa classe também contém o construtor default (linhas 9 a 10), que possibilita criar um objeto sem informar os valores dos campos. Para definir o valor de cada Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Listagem 5.  Código da classe FornecedorDto. 01. package negocio.dto; 02. 03. public class FornecedorDto { 04. int codigoFornecedor; 05. String endereco; String telefone; 06. String nome; 07. 08. 09.  public  FornecedorDto() { } 10. 11. 12.  public  String toString() { 13.  return codigoFornecedor + “ - “ + nome; } 14. 15. 16.  public int getCodigoFornecedor() { 17.  return codigoFornecedor; 18. } 19. 20.  public void setCodigoFornecedor( int codigoFornecedor) { 21.  this.codigoFornecedor = codigoFornecedor; } 22. 23. 24.  public  String getEndereco() { campo podem ser utilizados os respectivos setters , definidos nesta classe. E para recuperar o valor de um campo, pode ser utilizado o getter correspondente. Outro método presente na classe FornecedorDto é o toString(). Este retorna uma representação na forma de String do objeto e será usado na camada de apresentação para criar uma descrição curta de um fornecedor. Em seguida, classes semelhantes devem ser criadas para implementar os DTOs das unidades de medida, produtos, pedidos e itens pedidos. Como existe uma relação entre as classes Produto e Unidade , conforme indicado no diagrama de classes (vide Figura 5), em ProdutoDto é criada uma referência para UnidadeDto , de forma que cada produto tenha uma unidade de medida. O mesmo ocorre em PedidoDto , o qual, além dos campos especificados no diagrama de classes, possui o campo fornecedor , de tipo FornecedorDto , por causa do relacionamento da classe Pedido com Fornecedor , sinalizando um pedido de compra. Concluindo a implementação dos DTOs, um raciocínio semelhante pode ser verificado em ItemPedidoDto , que declara os atributos produto e pedido , derivados dos relacionamentos de ItemPedido com Produto e Pedido. Outro padrão de projeto usado na aplicação exemplo é o DAO. Este define objetos que encapsulam o código de acesso ao banco de dados. Para tanto, os DAOs acessam a camada de persistência para obter a conexão com o banco, executar SQLs para inserir, editar, excluir ou consultar registros, entre outras funções. Nas Listagens 6 e 7 é apresentado o código da classe UnidadeDao. Nas linhas 13 a 17 dessa classe são declarados um PreparedStatement para cada SQL definido em UnidadeSQL , pertencente à camada de persistência. Um ganho importante com isso é que 25.  26. 27. 28.  29.  30. 31. 32.  33.  34. 35. 36.  37.  38. 39. 40.  41.  42. 43. 44.  45.  46. 47. } return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } através de PreparedStatements os scripts SQL de inserção, edição, exclusão e consulta são executados mais rapidamente, pois o banco de dados os mantém preparados em memória, alterando apenas os parâmetros a cada vez que são chamados. Os PreparedStatements declarados em UnidadeDao são inicializados no construtor através da chamada ao método prepararStatement() , da classe DatabaseManager , deixando os mesmos SQLs prontos no banco de dados para execução quando a aplicação estiver rodando. Assim não será preciso construir novamente os SQLs caso seja criada uma nova instância de UnidadeDao. Note, ainda, que a inicialização dos mesmos acontece no construtor. Nas linhas 29 a 39 é declarado o método criarUnidade(). Este recebe UnidadeDto  como parâmetro, a partir deste define os parâmetros no PreparedStatement inser t e executa o SQL para criar uma nova unidade de medida no banco de dados. De forma análoga, os métodos editarUnidade() , excluirUnidade() e consultarUnidade()   recebem um DTO como parâmetro, alimentam o PreparedStatement com os valores por ele esperados e executam o SQL correspondente para aplicar as alterações no  banco de dados. Uma exceção é o método consultarTodasUnidades() , que não recebe parâmetros por ser responsável por consultar e retornar todos os registros da tabela Unidade. O código de FornecedorDao segue a mesma proposta de UnidadeDao e, portanto, não será exibido. Já o código de ProdutoDao é apresentado nas Listagens 8 e 9. Essa classe funciona de forma análoga aos demais DAOs, porém o tratamento do seu campo unidade (do tipo UnidadeDto) é diferente, uma vez que ele é proveniente de um relacionamento entre as classes ProdutoDto e UnidadeDto. Sendo assim, o código da unidade é obtido através da chamada ao método getCodigoUnidade()  do objeto unidade. Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 9 UML e Java na prática Além disso, nas consultas, para cada objeto do tipo ProdutoDto criado, é criado também um UnidadeDto , que é consultado no  banco de dados usando o DAO UnidadeDao informado no construtor. Deste modo, a informação consultada não se restringe apenas aos dados do produto, pois também são buscados os dados da unidade de medida do mesmo. Com código semelhante, temos ainda os DAOs PedidoDao e ItemPedidoDao , que foram omitidos por questão de espaço. Concluída essa etapa, é válido ressaltar que podemos implementar o código relacionado ao acesso a dados de uma maneira mais simples, explorando frameworks Java criados exatamente com esse intuito. Um bom exemplo é o Hibernate, que abstrai, dentre outras coisas, a necessidade de escrita do código SQL. Dito isso, a partir de artigos já publicados nesta revista sobre esse tema, o leitor pode buscar realizar essa refatoração em nosso exemplo e consequentemente explorar novos conceitos. Listagem 6. Código da classe UnidadeDao – Parte 1. 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. package negocio.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import database.DatabaseManager; import database.UnidadeSQL; import negocio.dto.UnidadeDto; public class UnidadeDao { private PreparedStatement insert; private PreparedStatement update; private PreparedStatement delete; private PreparedStatement selectAll; private PreparedStatement select; public UnidadeDao(DatabaseManager databaseManager) throws SQLException { insert = databaseManager.prepararStatament(UnidadeSQL.insert); update = databaseManager.prepararStatament(UnidadeSQL.update); delete = databaseManager.prepararStatament(UnidadeSQL.delete); select = databaseManager.prepararStatament(UnidadeSQL.select); selectAll = databaseManager.prepararStatament(UnidadeSQL.selectAll); } public void criarUnidade(UnidadeDto unidade){ try { insert.setInt (1, unidade.getCodigoUnidade()); 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. insert.setString(2, unidade.getSigla()); insert.setString(3, unidade.getDescricao()); insert.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } public void editarUnidade(UnidadeDto unidade){ try { update.setInt (1, unidade.getCodigoUnidade()); update.setString(2, unidade.getSigla()); update.setString(3, unidade.getDescricao()); update.setInt (4, unidade.getCodigoUnidade()); update.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } public void excluirUnidade(UnidadeDto unidade){ try { delete.setInt (1, unidade.getCodigoUnidade()); delete.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } Listagem 7. Código da classe UnidadeDao – Parte 2. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. public ArrayList consultarTodasUnidades() { ArrayList lista = new ArrayList(); try { ResultSet rs = selectAll.executeQuery(); while (rs.next()) { UnidadeDto unidade = new UnidadeDto(); unidade.setCodigoUnidade(rs.getInt(“codigoUnidade”)); unidade.setSigla(rs.getString(“sigla”)); unidade.setDescricao(rs.getString(“descricao”)); lista.add(unidade); } return lista; } catch (SQLException e) { e.printStackTrace(); return null; } } 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. } try { select.setInt(1, codigoUnidade); ResultSet rs = select.executeQuery(); if (rs.next()) { UnidadeDto unidade = new UnidadeDto(); unidade.setCodigoUnidade(rs.getInt(“codigoUnidade”)); unidade.setSigla(rs.getString(“sigla”)); unidade.setDescricao(rs.getString(“descricao”)); return unidade; } return null; } catch (SQLException e) { e.printStackTrace(); return null; } } public UnidadeDto consultarUnidade(int codigoUnidade) { 10 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Listagem 8. Código da classe ProdutoDao – Parte 1. 01. 02. 03. 04. 05. 06. 07. 08. 09. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. package negocio.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import database.DatabaseManager; import database.ProdutoSQL; import ne gocio.dto.ProdutoDto; import negocio.dto.UnidadeDto; public class ProdutoDao { private static PreparedStatement insert; private static PreparedStatement update; private static PreparedStatement delete; private static PreparedStatement selectAll; private static PreparedStatement select; private UnidadeDao unidadeDao; public ProdutoDao(DatabaseManager databaseManager, UnidadeDao unidadeDao) throws SQLException { this.unidadeDao = unidadeDao; synchronized (this) { insert = databaseManager.prepararStatament(ProdutoSQL.insert); update = databaseManager.prepararStatament(ProdutoSQL.update); delete = databaseManager.prepararStatament(ProdutoSQL.delete); select = databaseManager.prepararStatament(ProdutoSQL.select); selectAll = databaseMana ger.prepararStatament(ProdutoSQL.selectAll); } } 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. public void criarProduto(ProdutoDto produto){ try { insert.setInt (1, produto.getCodigoProduto()); insert.setString(2, produto.getNomeProduto()); insert.setString(3, produto.getCodigoBarras()); insert.setFloat (4, produto.getEstoque()); insert.setString(5, produto.getDescricao()); insert.setFloat (6, produto.getValorProduto()); insert.setInt (7, produto.getUnidade().getCodigoUnidade()); insert.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } public void editarProduto(ProdutoDto produto){ try { update.setInt (1, produto.getCodigoProduto()); update.setString(2, produto.getNomeProduto()); update.setString(3, produto.getCodigoBarras()); update.setFloat (4, produto.getEstoque()); update.setString(5, produto.getDescricao()); update.setFloat (6, produto.getValorProduto()); update.setInt (7, produto.getUnidade().getCodigoUnidade()); update.setInt (8, produto.getCodigoProduto()); update.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } Listagem 9. Código da classe ProdutoDao – Parte 2. 01. 02. 03. 04. 05. 06. 07. 08. 09. public void excluirProduto(ProdutoDto produto){ try { delete.setInt (1, produto.getCodigoProduto()); delete.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 30. } 31. 32. } 33. 34. 35. 36. 37. 38. public ArrayList consultarTodosProdutos() { ArrayList lista = new ArrayList(); try { ResultSet rs = selectAll.executeQuery(); while (rs.next()) { ProdutoDto produto = new ProdutoDto(); produto.setCodigoProduto(rs.getInt(“codigoProduto”)); produto.setNomeProduto(rs.getString(“nomeProduto”)); produto.setCodigoBarras(rs.getString(“codigoBarras”)); produto.setEstoque(rs.getFloat(“estoque”)); produto.setDescricao(rs.getString(“descricao”)); produto.setValorProduto(rs.getFloat(“valorProduto”)); produto.setUnidade(unidadeDao.consultarUnidade(rs.getInt (“codigoUnidade”))); lista.add(produto); } return lista; } catch (SQLException e) { e.printStackTrace(); return null; 29. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. public ProdutoDto consultarProduto(int codigoProduto) { try { select.setInt(1, codigoProduto); ResultSet rs = select.executeQuery(); if (rs.next()) { ProdutoDto produto = new ProdutoDto(); produto.setCodigoProduto(rs.getInt(“codigoProduto”)); produto.setNomeProduto(rs.getString(“nomeProduto”)); produto.setCodigoBarras(rs.getString(“codigoBarras”)); produto.setEstoque(rs.getFloat(“estoque”)); produto.setDescricao(rs.getString(“descricao”)); produto.setValorProduto(rs.getFloat(“valorProduto”)); produto.setUnidade(unidadeDao.consultarUnidade(rs.getInt(“codigoUnidade”))); return produto; } return null; } catch (SQLException e) { e.printStackTrace(); return null; } } 56. } Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 11 UML e Java na prática Camada de Apresentação Figura 6. Tela principal da aplicação exemplo Figura 7. Tela do cadastro de fornecedores A camada de apresentação proposta para a aplicação exemplo foi implementada usando Swing. No entanto, outras tecnologias poderiam ter sido empregadas como, por exemplo, AWT, JavaFX ou, ainda, considerando-se que a camada de apresentação se torne acessível via web, tecnologias como JSP ( JavaServer Pages) e  JSF ( JavaServer Faces). As telas da aplicação serão mostradas nas imagens a seguir, mas o código-fonte foi omitido, por não fazer parte do escopo deste artigo. Na Figura 6 temos a tela principal do software, que permite o acesso a todos os casos de uso da aplicação de controle de estoque. Ao selecionar a primeira opção, Cadastro de Fornecedor , a janela exposta na Figura 7 é apresentada – a qual contém todos os casos de uso relacionados a fornecedor (vide Figura 1) – oferecendo acesso às operações de criação, edição e exclusão de fornecedores. O mesmo vale para as telas exibidas nas Figuras 8 , 9 e 10 , que viabilizam o acesso às funcionalidades de listagem, criação, edição e exclusão de unidades de medidas, produtos e pedidos, respectivamente. Como verificado, a linguagem UML é uma poderosa ferramenta de modelagem de software e apresenta diversos diagramas que auxiliam as atividades de desenvolvimento. Consequentemente, sua adoção possibilita um aumento da qualidade da documentação produzida nas fases de análise e projeto, assim como uma melhora da legibilidade e manutenibilidade do código, da comunicação entre os desenvolvedores, entre outros fatores. Diante disso, empresas que primam por qualidade e bons pro jetos de software exigem dos desenvolvedores cada vez mais o domínio dos principais diagramas dessa linguagem de modelagem, pois isso implica diretamente na boa conversão dos mesmos em código-fonte correto e eficiente. Autor  John Soldera Figura 8. Tela do cadastro de unidades de medida  [email protected] É bacharel em Ciências da Computação pela UCS (Universidade de Caxias do Sul), mestre em Computação Aplicada pela Unisinos e doutor em Ciências da Computação pela UFRGS (Universidade Federal do Rio Grande do Sul). Trabalha com Java há 12 anos e possui a certificação SCJP. Links: Figura 9. Tela do cadastro de produtos  Javadoc da plataforma Java SE 8. https://docs.oracle.com/javase/8/docs/api/  Página oficial do Hibernate. http://hibernate.org/  Página oficial do MySQL. https://www.mysql.com/  Figura 10. Tela do cadastro de pedidos 12 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Refatorando código com padrões de projeto Veja neste artigo uma abordagem prática para melhorar a qualidade do código com os padrões Prototype, Strategy e Adapter O desenvolvimento de software de qualidade é um processo que exige tempo e um elevado esforço por parte da equipe do projeto. Para manter o padrão alto é primordial evitar o uso de métodos antiquados ou inapropriados, como codificar de forma estruturada. Além disso, é necessário escrever código pensando também na sua facilidade de manutenção e evolução. Com essas medidas o processo de desenvolvimento tende a se tornar mais caro, mas o custo para corrigir os problemas e manter o software será ainda mais alto sem aplicá-las. Em sistemas em que não houve essa preocupação a refatoração será um processo bastante complexo ou mesmo inviável, pois o código provavelmente apresentará  baixa coesão, alto acoplamento, entre outros indícios que sinalizam a baixa qualidade. Em alguns casos chega a ser mais aconselhável criar um novo sistema do que tentar modificá-lo. Diante disso, aplicar técnicas que amenizem a produção de código ruim é essencial, como a orientação a objetos e padrões de projeto, utilizadas para alcançar um alto nível de satisfação na produção do código. Essas técnicas, se aplicadas corretamente, permitirão um código mais fácil de ser mantido. Além disso, permitirá que o sistema seja estendido, isto é, receba novas funcionalidades, de forma mais simples. Com base no que foi apresentado, este artigo dará uma visão geral sobre padrões de projeto e como eles são utilizados na linguagem Java. Em seguida, explanará sobre como refatorar um projeto exemplo aplicando os padrões Prototype, Strategy e Adapter. Fundamentos da orientação a objetos O paradigma da orientação a objetos estabelece fundamentos que influenciam diretamente a organização e qualidade do código, como abstração, encapsulamento e herança. O conceito da abstração, por exemplo, pos- Fique por dentro A utilização de padrões de projeto permite o desenvolvimento de um código limpo, flexível e testável, assim como facilita a comunicação entre arquitetos e desenvolvedores ao viabilizar um vocabulário comum, amenizando possíveis problemas de interpretação, algo presente em projetos de software. Com base nisso, este artigo é útil por apresentar uma maneira prática para detecção de problemas de design no código e como refatorá-lo empregando boas práticas ao introduzir padrões de projeto ao ciclo de desenvolvimento. sibilita focar no que é um objeto e qual comportamento ele terá antes mesmo de começar a ser implementado.  Já o encapsulamento consiste em adicionar segurança à aplicação. Isso porque através deste conceito haverá a separação entre aspectos externos e implementações internas de um objeto, como viabilizado pelos modificadores de acesso. Assim, evita-se que o software crie uma interdependência excessiva, que normalmente chamamos de alto acoplamento, entre objetos e que uma mudança possa gerar efeitos colaterais em todo o sistema. O conceito de herança, por sua vez, proporciona que estruturas comuns sejam compartilhadas entre diferentes classes, e assim, métodos e atributos podem ser reutilizados, evitando possíveis problemas como código duplicado. Esses fundamentos da orientação a objetos, junto com os conceitos de padrões de projeto, ajudarão o profissional a produzir u m código de qualidade e a reduzir o esforço necessário no decorrer do desenvolvimento do software e de sua manutenção. Padrões de projeto ou Padrões GoF O conceito “padrão de projeto” surgiu com o arquiteto austríaco Christopher Alexander. Ele constatou que por conta do emprego de padrões as cidades medievais eram mais bonitas e harmônicas. O resultado dessa observação foi a publicação do livro “ A Pattern Language ”, em meados de 1970, em que ele apresenta como utilizar padrões em construções futuras ou na melhoria das já existentes. Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 13 Refatorando código com padrões de projeto  Já no âmbito da Engenharia de Software, o termo começou a ser utilizado por Kent Beck e Ward Cunningham, em meados de 1980. Porém, somente em 1995, quando Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides publicaram o livro “ Design Patterns – Elements of Reusable Object-Oriented Software ”, considerado a referência mais importante sobre o assunto, os padrões de projeto começaram a ter notoriedade na área da computação. Após isso, os autores ficaram conhecidos como a “A Gangue dos Quatro”, ou Gang of Four (GoF). Foi daí que surgiu a nomenclatura “Padrões GoF”. O livro está dividido em três categorias, a saber: padrões de criação, estruturais e comportamentais. Ao todo são vinte e três padrões catalogados e que descrevem soluções para diversos problemas recorrentes durante o processo de desenvolvimento, como será visto a seguir. Padrões de criação Os padrões de criação têm a função de abstrair o processo de criação de objetos e podem ser divididos em duas categorias: classe e objeto. A utilização desses padrões auxilia o sistema a ser independente de como seus objetos são criados, compostos e representados. Isso ocorre devido ao encapsulamento do conhecimento sobre as classes concretas que serão usadas pela aplicação e também pela ocultação do modo como essas classes são criadas e formadas. A Tabela 1  apresenta os seis padrões criacionais e os seus objetivos. Padrões estruturais Como o próprio nome diz, os padrões dessa categoria estão relacionados com a estrutura dos objetos. Eles se preocupa m com a composição das classes e dos objetos e com o relacionamento entre eles. O objetivo destes padrões, descritos na Tabela 2 , é facilitar o design do sistema identificando maneiras de realiza r o relacionamento entre as entidades, deixando o desenvolvedor livre desta preocupação. Padrões de projeto Objetivo Abstract Factory Prover uma interface para criar uma família de objetos relacionados ou dependentes sem especificar suas classes concretas. O Abstract Window  Toolkit (AWT) utiliz a esse padr ão para for necer um con junto de classe s indepen dentes de p lataforma e specíf icas para op erações grá ficas. Separar o processo de construção de objetos complexos de sua representação, de modo que um mesmo processo possa gerar diferentes representações de forma transparente. Um bom exemplo é a classe java.lang.ProcessBuilder, utilizada para executar comandos em sistemas Builder operacionais. Factory Method Encapsular a escolha da classe concreta a ser utilizada na criação de um determinado tipo de objeto. O método Calendar.getInstance() é uma implementação da proposta definida pelo Factory Method, pois ele encapsula a forma de criar um novo objeto concreto (Calendar). Permitir a criação de um novo objeto através da cópia das propriedades de um objeto existente. Em outras palavras, ele usa uma instância de Prototype Singleton uma classe para criar uma nova, mas que possui propriedades idênticas à inicial. As classes que implementam a interface java.lang.Cloneable, por exemplo, utilizam o método java.lang.Object.clone() para criar cópias de si mesma. Garantir que uma classe tenha somente uma instância e fornecer um ponto global de acesso à mesma. A classe java.lang.Runtime é um exemplo de Singleton implementado pela JVM, pois cada aplicativo Java tem uma única instância dessa classe que permite interação entre o aplicativo e o ambiente em que é executado. Tabela 1. Padrões de projetos criacionais Padrões de projeto Objetivo Converter a interface de uma classe em outra que possa ser utilizada por clientes. Assim, classes com interfaces incompatíveis poderão trabalhar Adapter em conjunto, através da interface de adaptação. Por exemplo, o java.io.InputStreamReader recebe uma cadeia de bytes (java.io.InputStream) e os adapta para caracteres (java.io.Reader). Desacoplar uma abstração de sua implementação, de modo que ambas variem de maneira independente. Esse padrão é facilmente encontrado Bridge nas bibliotecas de drivers como, por exemplo, as que fazem acesso JDBC ao banco de dados. Composite Agrupar objetos que fazem parte de uma relação parte-todo de forma a tratá-los sem distinção. O método addAll(Collection) de java.util.List implementa o padrão Composite para adicionar os objetos de uma coleção ao final da lista especificada. Permitir que funcionalidades sejam adicionadas a um objeto existente em tempo de execução, sem alterar a sua estrutura. Em outras palavras, Decorator esse padrão cria uma classe decoradora que irá envolver a classe original e fornecer métodos adicionais para manipulá-la, a fim de manter sua estrutura intacta. Um exemplo de aplicação real desse design pattern é a classe java.io.BufferedOutputStream, pois ela recebe em seu construtor um objeto java.io.OutputStream e o “decora”. Encapsular o acesso a uma funcionalidade de um sistema ou módulo, a fim de simplificar seu uso e fornecer uma interface mais amigável. A Facade classe java.awt.Graphics, por exemplo, é uma fachada que oferece métodos simples para as operações relacionadas aos gráficos. Compartilhar, de forma ef iciente, objetos que são usados em grande quantidade. Assim é possível reduzir o número de objetos criados, aumen- Flyweight tar o desempenho e amenizar o consumo da memória. O método valueOf(long) da classe java.lang.Long é um ótimo exemplo de implementação desse padrão, pois ele faz uso de cache interno. Controlar as chamadas a um objeto por meio de outro de mesma interface, ou seja, ele fornece um objeto substituto (proxy) ao verdadeiro. Proxy Assim, esse proxy poderá interceptar as chamadas aos métodos originais e adicionar ações antes ou depois do seu uso, como validar a restrição de segurança. Essa é a ideia do java.lang.reflect.Proxy. Tabela 2. Padrões de projetos estruturais 14 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Padrões comportamentais Os padrões comportamentais fornecem estratégias consolidadas para modelar a maneira como os objetos colaboram entre si. A partir disso, viabilizam a redução do acoplamento e tornam o código mais preparado para receber novas funções/extensões. Vejamos um exemplo para facilitar a compreensão dessa definição: imagine um sistema que possui suas configurações em um arquivo de texto e todas as vezes que esse arquivo for alterado é preciso que o sistema seja reconfigurado com os novos valores. Para atender esse requisito, o padrão de projeto Observer foi implementado. Assim, sempre que o arquivo for alterado o sistema será notificado para atualizar seu comportamento de acordo com as novas especificações. Estes padrões, descritos na Tabela 3 , tratam da forma como as entidades se comportam. Isto é, eles auxiliam a comunicação entre os objetos distribuindo as responsabilidades e definindo a comunicação interna. Aplicando os padrões Prototype, Strategy e Adapter Padrão de projeto não é código pronto para ser reaproveitado no desenvolvimento de software. Na verdade, ele é um modelo abstrato para resolver determinado problema. Dito isto, a part ir Padrões de projeto de agora será apresentado um exemplo de um jogo de robôs com problemas arquiteturais que podem ser solucionados através de refatoração aplicando os padrões Prototype , Strategy e Adapter. Imagine que esse jogo era implementado por uma empresa e foi vendido para outra que tem em sua equipe de desenvolvimento profissionais com bastante experiência. A partir de agora esses profissionais estarão encarregados de melhorá-lo, pois há relatos que o  jogo é lento quando há muitos robôs participando de uma partida. O jogo consiste em uma batalha onde os robôs podem se movimentar em um tabuleiro e ao detectar que há um oponente próximo ele deve realizar uma série de comportamentos para atingir o oponente ou fugir. Além disso, uma característica importante é que todos os robôs devem possuir os mesmos atributos, como força e velocidade. Essa regra foi definida para tornar a partida mais justa. Diante disso, os desenvolvedores fizeram uma análise no código e detectaram alguns pontos que podem ser melhorados com a adoção de padrões de projetos. Em seguida, para acompanhar a evolução do código, optaram por criar uma partida com apenas três robôs. Na Listagem 1 é apresentada a classe Jogo, artefato que representa o tabuleiro onde os robôs realizam as batalhas. Objetivo Chain of responsibility Promover o desacoplamento entre o objeto solicitante e o solicitado, dando oportunidade a diversos objetos de tratar a solicitação, ou seja, os objetos receptores serão encadeados e transmitidos à solicitação através de uma cadeia até que um objeto o trate. Um exemplo de aplicação desse padrão é o tratamento de exceções, no qual uma exceção recursivamente escala a pilha de métodos até encontrar um capaz de tratá-la. Command Encapsular uma solicitação como objeto, a fim de simplificar a execução encadeada de comandos. Assim, as operações podem ser enfileiradas, registradas ou desfeitas. As implementações de java.lang.Runnable e javax.swing.Action utilizam esse padrão. Interpreter  juntament e com um interpre tador, que, po r sua vez, us a a represe ntação pa ra compreen der sente nças dessa linguagem. As subclasses Dada uma linguagem, por exemplo, o formato de exibição de uma data, tem a função de definir uma representação para sua gramática de java.text.Format fazem uso do padrão Interpreter. Iterator Fornecer um modo eficiente para percorrer sequencialmente os elementos de uma coleção, sem que a estrutura interna dessa coleção seja exposta. Todas as implementações de java.util.Iterator fazem uso desse padrão. Definir um objeto que encapsula a forma como um conjunto de objetos interage. O Mediator promove o acoplamento fraco ao evitar Mediator que os objetos se refiram uns aos outros explicitamente e possibilita variar suas interações independentemente. Todos os métodos scheduleXXX() da classe java.util.Timer são exemplos de implementação desse design pattern. Memento Observer Capturar e externalizar o estado interno de um objeto, sem violar o encapsulamento, de modo que ele possa ser restaurado posteriormente. A classe java.util.Date é um exemplo para esse padrão, pois internamente faz uso de um long para armazenar o valor da data. Definir um mecanismo ef iciente para reagir às alterações realizadas em determinados objetos. A interface javax.faces.event.PhaseListener, por exemplo, permite notificar o início e o fim de cada fase do ciclo de uma requisição. Alterar o comportamento de um determinado objeto de acordo com o estado no qual ele se encontra. A classe javax.faces.lifecycle. State LifeCycle, por exemplo, tem dependência do estado do ciclo de vida do JSF. Assim, se a fase for para restaurar a cama de visão (Restore view), o comportamento será o de construir os elementos da tela, fazer validações nos componentes e salvar a view no FacesContext. Definir uma família de algoritmos, encapsular cada uma delas e torná-las intercambiáveis. Strategy propicia que o algoritmo varie inde- Strategy pendente dos clientes que o utilizam. O método compare() da interface java.util.Comparator, muito utilizado para ordenação de listas, é um exemplo prático da aplicação desse padrão. Definir a ordem na qual determinados passos devem ser realizados na resolução de um problema e possibilitar que eles possam ser exe -  Template Metho d cutados de formas diferentes de acordo com a situação. Todos os métodos não abstratos de java.io.InputStream, java.io.OutputStream,  java.io.Re ader e java. io.Writer implement am o Template Method . Permitir que uma classe visitante mude o algoritmo de execução de outra sem a necessidade de alterá-la. Dessa forma, a execução do al- Visitor goritmo pode variar de acordo com a classe visitante. Um exemplo é o javax.lang.model.element.ElementVisitor, que opera em conjunto com o objeto Element do mesmo pacote. Tabela 3. Padrões de projetos comportamentais Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 15 Refatorando código com padrões de projeto A intenção da equipe, com isso, foi isolar pequenos trechos do  jogo a fim de identificar, com mais facilidade, problemas que possam ser sanados com boas práticas. A primeira delas, inclusive, acabou de ser citada: dividir para conquistar. Listagem 2 . A implementação da classe Robo. public class Robo { static enum MovimentoEnum {  AGRESSIVO, DEFENSIVO, ALEATORIO } Listagem 1 . Código da classe Jogo que simula o tabuleiro de batalha dos robôs. private String nome; public class Jogo {  // Código com os demais atributos suprimido public static void main(String[] args) {  //Cria uma instância de robô   Robo rambo = new Robo(); rambo.setNome(“Rambo”);    //Cria outra instância de robô Robo chuckNorris = new Robo(); chuckNorris.setNome(“Chuck Norris”);  /** * Resgata os dados de um banco de dados para inicializar o robô. * Todos os robôs são iguais, exceto o nome! */ private void inicializar() {...}  //Cria mais uma instância de robô Robo bigHero = new Robo(); bigHero.setNome(“Big Hero 6”);    //Solicita que o robô se movimente de forma agressiva rambo.mover(Robo.MovimentoEnum. AGRESSIVO);  //Solicita que o robô se movimente de forma defensiva chuckNorris.mover(Robo.MovimentoEnum.DEFENSIVO);  //Solicita que o robô se movimente de forma aleatória bigHero.mover(Robo.MovimentoEnum. ALEATORIO);   }  Já na Listagem 2 , temos o código da classe Robo. Em seu construtor, note que há uma chamada ao método inicializar() , responsável por acessar um banco de dados e resgatar as informações necessárias para instanciar um robô, como força e velocidade. O método mover() , por sua vez, tem a responsabilidade de realizar a movimentação do robô. Para isso, ele recebe como parâmetro qual tipo de movimento o robô deverá executar. Ademais, esse método também possui a implementação dos algoritmos referentes ao movimento solicitado. Note que para cada movimento há uma lógica implementada na estrutura condicional if/else. Perceba também que somente a lógica de movimento aleatório (MovimentoEnum.ALEATORIO) , no método mover() , apresenta um trecho de código que invoca o método MovimentoAleatorio. execute(). Isso ocorre devido ao uso de uma biblioteca de terceiros que já implementava um algoritmo de movimentação aleatória para um robô. Os demais algoritmos de movimentação não foram analisados e por isso o código dos mesmos não foi exibido. A Listagem 3 apresenta o código da classe MovimentoAleatorio e como verificado, o método execute() implementa o algoritmo que realiza a movimentação aleatória de um robô. Por se tratar de uma biblioteca de terceiros, a equ ipe de desenvolvimento não tem acesso ao código fonte com a implementação real da classe. Portanto não é possível realizar modificações. Ao analisar o código das Listagens 1 , 2 e 3 , repare que ele possui uma série de anti-patterns , isto é, soluções para resolver problemas aplicando práticas muito utilizadas, mas que apesar de aparentemente benéficas, na verdade prejudicam a manutenibilidade, flexibilidade, reuso, entre outros. public void setNome(String string) { this.nome = nome; }  /** * Executa o comando de movimentação. * Pode ser um ataque, uma defesa ou movimentos aleatórios! * * @param acao A ação de movimentação. */ public void mover(MovimentoEnum acao) { if (MovimentoEnum. AGRESSIVO.equals(acao)) {  // Algoritmo para movimentação agressiva } else if (MovimentoEnum.DEFENSIVO.equals(acao)) {  // Algoritmo para movimentação defensiva } else if (MovimentoEnum. ALEATORIO.equals(acao)) {  // O algoritmo de movimentação aleatória é implementado  // por terceiros através do método MovimentoAleatorio.execute() new MovimentoAleatorio().execute(); } } } 16 public Robo() { inicializar(); } } Listagem 3 . Classe responsável pelo algoritmo de movimentaç ão aleatório do robô.  /**  * Implementação da estratégia de movimento aleatório.  */ public class MovimentoAleatorio { public void execute() { //Algoritmo para executar a movimentação aleatória } } O primeiro problema está relacionado à forma como o objeto Robo é criado, pois já que os robôs são todos igua is (exceto pelo nome) e o custo para criar um robô é elevado, devido à necessidade de acesso ao banco de dados, este anti-pattern pode ser resolvido com a aplicação do padrão de projeto Prototype. Através desse padrão os robôs poderão ser clonados, em vez de ser criada uma nova instância para cada um. Outro ponto questionável está relacionado ao método mover(). Note que ele acopla todo o conhecimento de como um robô se Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia movimenta. Desta forma, os algoritmos de movimentação ficam impossibilitados de serem reutilizados. Além disso, dificulta a manutenção do código, pois sempre que houver a necessidade de criar um novo tipo de movimento será necessário adicionar um if na estrutura do método mover. A solução para minimizar o acoplamento e facilitar o reuso do código está na aplicação do Strategy , pois com ele os algoritmos de movimentação serão abstraídos dentro de estratégias (classes) específicas. Por fim, o padrão de projeto Adapter pode ser utilizado para adaptar a classe MovimentoAleatorio em uma estratégia que será definida pelo padrão Strategy. Lembre-se que Adapter permite que estruturas diferentes possam trabalhar juntas através de uma classe adaptadora e como MovimentoAleatorio pertence a terceiros, ela não pode ser modificada pela equipe de desenvolvimento do jogo.  Aplicando o padrão Prototype O padrão de projeto Prototype tem como propósito criar novos objetos a partir de uma instância protótipo, ou seja, o objeto que implementa esse padrão é capaz de criar uma cópia de si mesmo. Uma aplicação para esse padrão pode ser verificada na manipulação de objetos com alta complexidade de serem instanciados e que são utilizados diversas vezes, com diferentes cópias. Por exemplo, simular diferentes modelos matemáticos utilizando dados financeiros de um país que, por sua vez, estão armazenados em uma base de dados. Ao aplicar esse padrão não é necessário carregar essas informações financeiras todas as vezes que um novo modelo for simulado. Para isso, uma instância protótipo pode ser criada com esses dados e sempre que for preciso uma cópia dela pode ser disponibilizada. Dito isso, esse pattern pode trazer ganhos significativos de performance para uma aplicação, pois só será necessário executar uma vez o processo complexo de criação desses objetos. A par tir de então, os demais objetos serão criados através da cópia da instância protótipo. A estrutura do padrão Prototype pode ser vista no diagrama de classes exposto na Figura 1. Ao relacionar essa estrutura com o código do jogo nota-se que a criação do objeto Robo (vide Listagem 2) tem um processamento muito alto para o sistema. Isso ocorre devido à necessidade de acessar a base de dados sempre que um robô é instanciado. Com a implementação do Prototype o acesso à base de dados será realizado somente para a criação da instância protótipo do Robo. Portanto, a classe Robo será refatorada para que fique com o código semelhante ao apresentado na Listagem 4. A primeira mudança a ser observada é a implementação da interface Cloneable. Essa interface indica que Robo será capaz de criar uma cópia de si mesma. A outra mudança é a implementação do método clone() , que possibilita a criação de um objeto idêntico ao objeto original. Listagem 4 . Aplicação do Prototype na c lasse Robo. public class Robo implements Cloneable { static enum MovimentoEnum {   AGRESSIVA, DEFENSIVA,  ALEATORIA  }  private  String nome;  public Robo() {   inicializar(); }  /** * Resgata os dados de um servidor para inicializar o robô. * Todos os robôs são iguais, exceto o nome! */  private void inicializar() {  }  public void setNome(String string) {   this.nome = nome;  }  /** * Executa o comando de movimentação. * Pode ser um ataque, uma defesa ou movimentos aleatórios! * * @param  acao A ação de movimentação. */  public void mover(MovimentoEnum acao) { if  (MovimentoEnum. AGRESSIVA.equals(acao)) { // Algoritmo para movimentação agressiva } else if  (MovimentoEnum.DEFENSIVA.equals(acao)) { // Algoritmo para movimentação defensiva } else if  (MovimentoEnum. ALEATORIA.equals(acao)) { // O algoritmo de movimentação aleatória é implementado // por terceiros através do método MovimentoAleatorio.execute() new MovimentoAleatorio().execute(); }  }  /** * Cria e retorna a cópia do objeto Robo */  @Override  public Robo clone(){ return this.clone();  } } Figura 1. Diagrama de classe do padrão Prototype – Fonte: http://www.dofactory.com Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 17 Refatorando código com padrões de projeto Após a refatoração da classe Robo é preciso atualizar o código da classe  Jogo para que façamos uso dos benefícios adquiridos com a adoção de Prototype, como pode ser visto na Listagem 5. Esta alteração consiste em criar somente um objeto robô utilizando o operador new. Os demais robôs serão criados a partir do método clone(). Listagem 5 . Refatoração do jogo para utilização do método clone(). public class Jogo {  public static void main(String[] args) { //Cria a instância protótipo   Robo robo = new Robo(); //Cria um novo robô através do clone da instância protótipo   Robo rambo = robo.clone();   rambo.setNome(“Rambo”); Robo chuckNorris = robo.clone();   chuckNorris.setNome(“Chuck Norris”); No primeiro caso, as opções para se deslocar de um lugar para outro podem variar de acordo com a preferência do usuário, que pode escolher fazer o camin ho a pé, carro ou ônibus. Assim, conforme a estratégia escolhida, o caminho para chegar ao destino pode ser diferente.  Já para o segundo caso (a ordenação de elementos em uma lista), lembre-se que pode haver uma quantidade muito grande de algoritmos de ordenação implementados e que eles podem ser escolhidos de acordo com cada situação. Aplicando os conceitos desse padrão para solução do problema arquitetural do método mover() (vide Listagem 2), a primeira alteração a ser realizada é criar as estratégias de movimentação dos robôs, conforme apresentado na Listagem 6. A interface MovimentoStrategy  representa o artefato Strategy exposto no diagrama da Figura 1 , e o método executar() é o responsável pela implementação da estratégia de movimentação.   Robo bigHero = robo.clone();   bigHero.setNome(“Big Hero 6”); rambo.mover(MovimentoEnum. AGRESSIVA);   chuckNorris.mover(MovimentoEnum.DEFENSIVA);   bigHero.mover(MovimentoEnum. ALEATORIA);  } }  Como verificado no exemplo, a utilização desse pattern possibilitou a redução do processamento gasto para criar objetos complexos. Note que não será necessário definir uma nova instância, utilizando o operador new , toda vez que for preciso criar um objeto.  Aplicando o padrão Strategy  O padrão Strategy trata diretamente de problemas relacionados ao acoplamento do código. Ele resolve esses problemas ao defin ir uma família de algoritmos, encapsular cada um deles e os tornar intercambiáveis. Assim, é estabelecida uma separação dos algoritmos de quem os usa, separação esta que possibilita escolher qual algoritmo utilizar de forma dinâmica. Por exemplo, a opção de gravar informações em um banco de dados ou em disco rígido pode variar de acordo com a disponibilidade de espaço. Portanto, a abordagem proposta na definição do Strategy é acoplar a classe que fará uso dos algoritmos apenas a uma abstração e não a uma implementação. Com isso, o código fica menos acoplado devido à possibilidade de ele utilizar diferentes estratégias sem conhecer como elas são implementadas. O diagrama da Figura 2 ilustra a organização desse padrão. De acordo com essa figura, a interface que representa a abstração da coleção de algoritmos é a Strategy. Ela é responsável por definir a assinatura do método a ser implementado pelas classes concretas (ConcreteStrategyA , ConcreteStrategyB e ConcreteStrategyC).  Já a classe Context é a classe que faz uso, de forma transparente, das estratégias implementadas nas classes concretas. Dois ótimos exemplos da aplicação desse padrão de projeto são o Google Maps e a ordenação de elementos em uma lista. 18 Figura 2. Diagrama de classe Strategy – Fonte: http://www.dofactory.com Listagem 6 . Definição da interface e implementação das classes de estratégias.  /**  * Interface que define a assinatura do método a ser * adotado para implementação das estratégias */ public interface MovimentoStrategy {  /** * Execução da movimentação definida pela estratégia. */  public void executar(); }  /**  * Implementação da estratégia de movimento agressivo. */ public class MovimentoAgressivo implements MovimentoStrategy{  @Override  public void executar() {  //Algoritmo para movimentação agressiva  } }  /**  * Implementação da estratégia de movimento defensivo. */ public class MovimentoDefensivo implements MovimentoStrategy{  @Override  public void executar() { //Algoritmo para movimentação defensiva  } } Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia  Já as classes MovimentoAgressivo  e MovimentoDefensivo correspondem à implementação concreta desses algoritmos. Essas estratégias, também expostas na última listagem referenciada, serão utilizadas para desacoplar o código do método mover(). Assim, a lógica condicional será removida e a movimentação do robô passará a ser executada conforme a estratégia enviada como parâmetro para esse método (vide Listagem 7). A partir disso, caso haja a necessidade de implementar uma nova ação de movimento, a classe Robo não precisará sofrer alterações, já que ela está acoplada apenas a uma abstração. Ou seja, será necessário apenas criar uma classe que implemente a interface MovimentoStrategy  e passá-la para o método  mover() como parâmetro. Assim como realizado anteriormente, após a refatoração na classe Robo é preciso alterar mais uma vez a classe  Jogo. Conforme a Listagem 8 , a mudança foi feita para enviar ao método mover() a instância da estratégia de movimento do robô. do como Wrapper , esse design pattern permite que classes com estruturas diferentes possam trabalhar em conjunto através da adequação da interface incompatível. O diagrama apresentado na Figura 3 exibe a estrutura desse padrão. Nele está presente a interface Target , que define a interface compatível para ser usada pela classe Client , e também a classe Adapter , responsável por encapsular e adaptar a Adaptee para que esta possa ser utilizada pelo Client , visto que sua interface é incompatível com a estrutura requerida pela classe cliente. Um exemplo desse padrão no mundo real pode ser encontrado nos adaptadores feitos para que os cartões de memória das máquinas fotográficas digitais sejam utilizados nos laptops. Outro exemplo interessante são as tomadas de energia. No Brasil, por exemplo, haviam vários modelos de tomadas e plugues antes Listagem 7 . Refatoração do método mover(). public class Robo implements Cloneable {  //Código suprimido...  /** * Executa o comando de movimentação, sem ter o conhecimento * de sua implementação. */  public void mover(MovimentoStrategy movimento) {  //Faz alguma coisa   movimento.executar(); //Faz mais alguma coisa  }  /** * Cria e retorna a cópia do objeto Robo */  @Override protected Robo clone(){ return this.clone();  } } Observe que após a implementação do padrão Strategy foi possível isolar os algoritmos de movimento do robô em classes separadas, facilitando a manutenção e desacoplando o código. Além disso, o padrão permite que as mesmas estratégias sejam reusadas em outros locais. Porém, com essa refatoração o jogo ficou sem a possibilidade de uso do movimento aleatório, fornecido pela classe MovimentoAleatorio. Isso ocorreu porque essa classe não implementa a interface MovimentoStrategy , e como se trata de uma classe de terceiros não é possível refatorá-la. Para conseguir que o jogo tenha as mesmas possibilidades de movimentação, o padrão  Adapter será adotado.  Aplicando o padrão Adapter  É exatamente para atender a necessidade de adaptar coisas que o padrão estrutural Adapter deve ser aplicado. Também conheci- Figura 3. Diagrama do padrão Adapter – Fonte: http://www.dofactory.com Listagem 8 . Refatoração para adequar a chamada ao método mover com as estratégias. public class Jogo {  public static void main(String[] args) { //Cria a instância protótipo   Robo robo = new Robo(); //Cria um novo robô através do clone da instância protótipo   Robo rambo = robo.clone();   rambo.setNome(“Rambo”); Robo chuckNorris = robo.clone();   chuckNorris.setNome(“Chuck Norris”);   Robo bigHero = robo.clone();   bigHero.setNome(“Big Hero 6”);  /** * O método mover() agora recebe a estratégia de movimentação, * ao invés de necessitar implementar o movimento. */   rambo.mover(new MovimentoAgressivo());   chuckNorris.mover(new MovimentoDefensivo());  /** * Código parou de compilar, pois a classe MovimentoAleatorio * não implementa a interface MovimentoStrategy */   bigHero.mover(new MovimentoAleatorio());  } } Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 19 Refatorando código com padrões de projeto da padronização realizada pela ABNT. Após a padronização, os produtos devem ter somente os plugues com a nova forma de três pinos redondos. Porém, muitas residências não possuem tomadas de três pinos para receber os aparelhos com esse tipo de plugue e para isso é necessário um adaptador.  Já um exemplo no desenvolvimento de software pode ser demonstrado no nosso jogo de robôs. Lembra que a adoção do padrão Strategy impossibilitou o uso da classe MovimentoAleatorio? A aplicação do Adapter pode solucionar esse problema. Correlacionando os artefatos desse padrão ao nosso exemplo, a interface MovimentoStrategy  corresponde à Target , e a classe MovimentoAleatorio  corresponde à Adaptee. Para completar, é necessário implementar a classe que fará a adaptação de MovimentoAleatorio  para ser utilizada junto às outras estratégias definidas pelo padrão Strategy. Para isso, criamos a classe MovimentoAleatorioAdaptado , apresentada na Listagem 9. Ela deve implementar a interface MovimentoStrategy e encapsular MovimentoAleatorio. Assim o  jogo poderá fazer uso do movimento fornecido por essa classe. Listagem 9 . Classe para adaptar MovimentoAleatorio.  /**  * Adaptador para encapsular a classe de movimento aleatório  * em uma estratégia  */ public class MovimentoAleatorioAdaptado implements MovimentoStrategy{ Listagem 10. Classe com a refatoração pa ra adaptar o movimento aleatório. public class Jogo {  public static void main(String[] args) { //Cria a instância protótipo   Robo robo = new Robo(); //Cria um novo robô através do clone da instância protótipo   Robo rambo = robo.clone();   rambo.setNome(“Rambo”); Robo chuckNorris = robo.clone();   chuckNorris.setNome(“Chuck Norris”);   Robo bigHero = robo.clone();   bigHero.setNome(“Big Hero 6”);  /** * O método mover agora recebe a estratégia de movimentação, * ao invés de necessitar implementar o movimento. */   rambo.mover(new MovimentoAgressivo());   chuckNorris.mover(new MovimentoDefensivo());  /** * A classe de movimento aleatório agora está adaptada * à interface MovimentoStrategy, permitindo o seu uso * no método de movimentaç ão do robô. */   bigHero.mover(new MovimentoAleatorioAdaptado());  } } Eles podem ser aplicados em situações do cotidiano, como demonstrado no artigo. E em sistemas computacionais, saiba que eles não foram especificados para serem aplicados em uma linguagem de programação específica. Uma prova disso é que no livro de referência do GoF os exemplos são ilustrados em C++.  private  MovimentoAleatorio comportamento; @Override  public void executar() { comportamento.execute();  } } Autor Por fim, a classe  Jogo (vide Listagem 10) precisa ser refatorada mais uma vez para que faça uso da nova classe MovimentoAleatorioAdaptado . Pronto! Com isso, todas as estratégias de movimentação voltam a ser utilizadas. Escrever código de qualidade depende de fatores como experiência da equipe de desenvolvimento e da aplicação de conceitos já difundidos, como é o caso dos padrões de projetos. Como prova disso, a refatoração que foi realizada no código do jogo resultou em um código menos acoplado, mais coeso e possibilitou a redução no processamento para se criar objetos complexos. Além disso, ficou muito mais simples criar novas possibilidades de movimentação para o robô e de testá-las. Isso ocorreu devido ao isolamento dos algoritmos de movimentação em classes separadas. Ainda que padrões de projeto sejam soluções abstratas para a resolução de determinados problemas, eles são uma referência que auxilia o profissional a produzir código de melhor qualidade. Ademais, eles propiciam uma linguagem de comunicação comum a ser utilizada pelos desenvolvedores para debater a resolução de problemas na construção de sistemas computacionais. Lembre-se que os padrões de projeto não foram definidos apenas para serem utilizados no âmbito computacional. 20 Gabriel Feitosa  [email protected] – www.gabrielfeitosa.com É Bacharel em Ciência da Computação pela Universidade Federal de Campina Grande (UFCG), Pós-Graduando em Engenhari a de Software na Faculdade 7 de Setembro (FA7), engenheiro de software, desenvolve aplicando conceitos como TDD e revisão em pares, especialista em Java e em aplicações que utilizam o conceito de Single-Page Application (SPA). Links e Livros: Exemplos práticos de padrões de projetos com foco na plataforma Java. http://  www.tutorialspoint.com/design_pattern/index.htm Coletânea de exemplos práticos com UML de padrões de projetos aplicados à linguagem Java. http://www.oodesign.com/  Design Patterns: Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Addison-Wesley, 1995. Livro de referência quando o assunto é padrões de projeto. Head First Design Patterns, Eric Freeman, Elisabeth Robson, Bert Bates, Kathy Sierra, 2004. Livro que exemplifica os padrões usando comparações com o mundo real. Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Aprenda a utilizar o Spring Web Flow Saiba como implementar fluxos de navegação em uma aplicação Web com um exemplo usando Spring e Hibernate F rameworks Model-View-Controller são vastamente utilizados e seus benefícios são amplamente conhecidos. Por exemplo, a forma como o padrão MVC distribui responsabilidades nas camadas da aplicação faz com que tenhamos camadas mais magras e, consequentemente, código mais organizado e manutenção mais simples. No Struts vemos isso ilustrado na medida em que escrevemos código Java em actions , ao invés de manter grandes parte da lógica do negócio em páginas JSP. No entanto, uma das principais vantagens da maioria dos frameworks MVC não é tão visível: a possibilidade de definir fluxos de navegação, ou seja, descrever con juntos de passos para os usuários no website de forma estruturada e reusável. Neste cenário, o Struts e o Spring, por exemplo, encapsulam os detalhes a respeito do atendimento das requisições de um usuário e renderizam páginas de resposta por meio da conf iguração de actions (ou handlers) de acordo com o estado da requisição. Isso pode ser notado quando temos a exibição de uma página de erro ou de sucesso dependendo da ação realizada pelo usuário. Fluxos simples como o do exemplo citado são perfeitamente gerenciáveis pelos mecanismos nativos da maioria dos frameworks MVC. Contudo, na medida em que a aplicação web se torna maior, com mais páginas ou com mais opções de respostas para as ações disponíveis, gerenciar esses fluxos pode se tornar mais complicado, uma vez que os frameworks MVC mais conhecidos não gerenciam os estados das requisições (vide BOX 1), tornando necessário escrever mais código nos handlers para fazer esse trabalho. Outro aspecto interessante sobre o gerenciamento do fluxo de navegação é que, se a estrutura da aplicação é complexa, ou seja, se os caminhos para chegar às páginas são longos e/ou muito subdivididos, entender o fluxo se torna mais difícil. Nesses casos, seria necessário manter um mapa atualizado das páginas e Fique por dentro Apresentaremos neste artigo o desenvolvimento de um projeto web que adota o padrão Model-View-Controller com o Spring Web Flow (SWF), parte do framework Spring MVC destinada à implementação de fluxos de navegação em aplicações web. O SWF será utilizado para tornar possível a definição de sequências de passos que guiam o usuário pelas páginas do website com pouca escrita de código Java, de forma reusável e de fácil manutenção. Deste modo, esse artigo é importante também por demonstrar como o SWF é integrado ao Spring MVC, bem como ao Hibernate ORM na construção de uma aplicação web completa. constantemente verificar o código para entender os fluxos, especialmente aqueles que envolvem condições e múltiplos estados. Ademais, a manutenção de grandes sites pode ser complicada e envolver bastante tempo estudando seus fluxos antes de implementar uma modificação. Com base nisso, neste artigo vamos explorar uma solução dada pelo Spring para o gerenciamento do fluxo de navegação em websites: o Spring Web Flow. Iniciado como uma extensão do Spring Framework, sua primeira versão estável foi lançada em 2006, no entanto, sua popularização ocorreu apenas com a versão 2, lançada em 2008. Com o SWF o fluxo de navegação é construído por meio da definição dos estados pelos quais o usuário passa. A part ir disso, para chegar em cada um dos estados o usuário dispara eventos. Por sua vez, entre os eventos que fazem as passagens de um estado BOX 1. Estado das requisições em aplicações web Numa aplicação web cada estado de uma requisição representa um propósito específico e, por isso, terá uma página ou handler correspondente. Por exemplo, a submissão de uma página de formulário pode ter quatro ou mais estados possíveis, a saber: o estado inicial (a exibição do formulário em si); aguardando (quando o formulário está sendo submetido, após o clique no botão de submissão); sucesso (que resultará na exibição de uma página de sucesso); ou insucesso (que resultará na exibição de uma página de erro). Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 21 Aprenda a utilizar o Spring Web Flow para outro temos as transições de estado. Todo esse mecanismo é especificado em regras dentro de um arquivo XML, o que viabiliza o reuso e diminui a escrita de código. No exemplo que apresentaremos nesse artigo focaremos nas principais vantagens do SWF ilustrando as maiores contribuições dadas por esse projeto ao desenvolvimento web, além de demonstrar seu uso de forma integrada com o Spring MVC e Hibernate. Dessa forma, nosso exemplo ficará bastante c ompleto e fornecerá ao leitor todos os passos necessários para iniciar o desenvolvimento com os frameworks citados, assim como algumas noções sobre padrões arquiteturais bastante utilizados em soluções dessa natureza. Detalhes do projeto exemplo O projeto será construído com a IDE Eclipse, persistirá dados em um banco de dados MySQL, rodará no Tomcat 8 e sua implementação seguirá o padrão Model-View-Controller, com o suporte do Spring MVC 4.2.2. A arquitetura da aplicação inclui ainda uma camada de serviço rudimentar, responsável por fazer a ligação entre a camada de controle e a camada de dados usando uma simplificação do padrão Service Layer. A camada de persistência seguirá o padrão DAO e utilizaremos o framework Hibernate ORM 4.3.11 para implementá-la. Já a nossa versão do SWF será a 2.4.0. Além disso, sugerimos o uso do Eclipse em versão superior à 4.4 ( Luna , para desenvolvimento  Java EE ) por já possuir integração nativa com o gerenciador de dependências Maven. Caso essa não seja a sua versão do Eclipse, é necessário instalar o Maven em versão igual ou superior à 3.2 (e/ou um plugin respectivo do Maven para Eclipse) ou gerenciar as dependências do projeto manualmente. Sendo assim, mais do que demonstrar o uso do SWF, apresentaremos um exemplo completo da integração entre Spring Web Flow, Spring MVC e Hibernate. No entanto, é importante destacar que não é objetivo desse artigo discorrer sobre as arquiteturas e padrões de projeto, bem como sobre os frameworks Spring e Hibernate. Dessa forma, o leitor deve explorar a documentação correspondente ou artigos anteriores publicados nessa revista sobre o uso desses recursos. Figura 1. Janela de seleção de tipo de projeto no Eclipse Criando o projeto e configurando as dependências Primeiramente, criemos um Dynamic Web Project  no Eclipse, o qual chamaremos de GestaoAcademicaWeb. Para isso, após acessar as opções de menu File > New Project > Other , na janela New Project , selecione Web > Dynamic Web Project , conforme a Figura 1. Clicando em Next será exibida a janela com as propriedades do projeto (vide Figura 2). Vamos configurá-lo para rodar no container Tomcat 8, no entanto, o leitor deve ficar à vontade para utilizar versões diferentes do Tomcat ou outro container web Java, como Jetty ou JBoss. Feito isso, clique em Finish. Com o projeto criado, vamos integrá-lo ao gerenciador de dependências Maven. Portanto, clique com o botão direito sobre o projeto e selecione Configure > Convert to Maven Project , como 22 Figura 2. Janela de propriedades do projeto mostra a Figura 3. Será criado, assim, o arquivo  pom.xml , onde vamos inserir as dependências do nosso projeto, conforme descrito na Listagem 1. Repare que incluímos nesse arquivo todas as dependências necessárias para utilizar o Spring MVC e o Hibernate ORM, assim como o driver JDBC  solicitado para a conexão com o banco Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia de dados. Em seguida, devemos criar também os arquivos \src\ hibernate.cfg.xml  e spring-context.xml , que encapsulam as configurações do Hibernate e do Spring MVC e cujos códigos-fontes podem ser vistos junto ao código disponível para download na página desta edição. Em hibernate.cfg.xml serão feitas as configurações correspondentes ao mapeamento objeto-relacional (Vide BOX 2), como veremos ao longo do artigo. Já o arquivo spring-context.xml inclui as configurações do Spring, como o diretório em que as páginas da camada de visão serão armazenadas e, futuramente, configu rações do SWF. Também aproveitamos esse arquivo para armazenar as configurações que o Hibernate usa para acessar o banco de dados como, por exemplo, o datasource. Agora, vamos definir o arquivo \WEB-INF\jdbc.properties para armazenar as configurações de acesso ao banco, como URI, usuário e senha (vide Listagem 2). As propriedades contidas nesse arquivo são, então, chamadas em spring-context.xml. BOX 2. Mapeamento Objeto-Relacional Mapeamento Objeto-Relacional é a técnica utilizada para tornar possível a representação das classes da aplicação – projetadas a partir do paradigma da Orientação a Objetos – em entidades de um banco de dados relacional – projetadas a partir do paradigma Relacional e da Normalização. Listagem 1. Código-fonte do arquivo pom.xml.   4.0.0   GestaoAcademicaWeb   GestaoAcademicaWeb   0.0.1-SNAPSHOT   war     src     4.2.2.RELEASE       org.springframework   spring-tx   4.2.2.RELEASE     org.springframework   spring-webmvc   4.2.2.RELEASE             org.springframework src   **/*.java             maven-compiler-plugin   3.1     1.7   1.7                       spring-orm 4.2.2.RELEASE org.springframework.webflow spring-webflow 2.4.0.RELEASE org.hibernate hibernate-core               maven-war-plugin 2.4     jstl 4.3.11.Final compile     WebContent   false               org.springframework   spring-context   4.2.2.RELEASE                               org.springframework   spring-web jstl 1.2 commons-dbcp commons-dbcp 1.4 mysql mysql-connector-java 5.1.6 Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 23 Aprenda a utilizar o Spring Web Flow Figura 4. Criando o pacote-base da aplicação Figura 3. Convertendo o projeto para Maven Neste ponto vale ressaltar que, embora não seja necessário seguir a ordem de criação dos arquivos sugerida nesse artigo, os passos que demonstramos simplificam o desenvolvimento, já que vemos que os arquivos fazem referências uns aos outros em a lguns casos. O próximo passo é criar a estrutura do projeto de acordo com os padrões que serão adotados; no nosso caso, uma arquitetu ra MVC, uma camada de serviço, além do padrão DAO para a camada de persistência. Sendo assim, clique com o botão direito sobre a pasta  Java Resources\src , no Package Explorer do Eclipse, e selecione New > Package para criar o pacote-base do projeto. Conforme demonstra a Figura 4 , digite “br.com.devmedia.gestaoacademicaweb” no campo Name e clique em Finish . Dentro do pacote-base vamos repetir o mesmo procedimento para criar os pacotes control e model , além do dao – onde serão armazenadas as classes da camada de persistência da nossa aplicação – e service – onde ficarão as classes da camada de serviço. Nossas páginas (a camada view) ficarão no diretório WEB-INF/  views. Por fim, criaremos, também, o diretório WEB-INF/flows , onde armazenaremos os fluxos. Dessa forma, a estrutura do projeto deve ficar como mostrado na Figura 5. O fluxo da aplicação exemplo Para começar, lembremos que, com SWF, um fluxo é composto por um conjunto de estados, cada estado terá uma página correspondente e as mudanças de estado e, consequentemente, de página, ocorrem após o usuário disparar eventos. A partir disso, saiba que esses eventos podem levar a dois tipos básicos de est ado: 24 Figura 5. Estrutura do projeto Listagem 2 . Código do arquivo jdbc.properties.  jdbc.driverClassName= com.mysql.jdbc.Driver  jdbc.dialect=org.hibernate.dialect.MySQLDialect  jdbc.databaseurl=jdbc:mysql://localhost:3306/gestaoacademica  jdbc.username=gestaoacademica  jdbc.password=gestaoacademica • View-state: representado por uma página na qual o usuário chega espontaneamente, por exemplo, digitando um endereço; • End-state: representado por uma página na qual o usuário chega como resultado de um evento, como clicar em um link ou como resposta a uma submissão de formulário. Mais além, os handlers responsáveis pelas submissões de formulários são representados como um “sub-estado” chamado action-state , que existe entre view-state e um end-state. Como nesse artigo criaremos um cadastro de docentes de uma universidade cujo fluxo é bastante simples, especificamos apenas três estados (dois view-states e um end-state), como segue: 1. View-state 1, representado pela página de listagem; Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 2. View-state 2, representado pelo formulário de cadastro de docentes; 3. End-state, representado pela página de resposta ao cancelamento. do pelas camadas de modelo e persistência e depois pelas camadas de serviço e controle. Na sequência, construiremos o f luxo com o SWF, objeto principal desse artigo. A Figura 6 demonstra o fluxo do nosso projeto exemplo, ilustrando seus estados e transições.  As camadas de modelo e persistência Vamos iniciar a nossa implementação criando, dentro do pacote model , a classe Docente , único  JavaBean do nosso projeto. Sendo assim, com o botão direito sobre o pacote, selecione a opção New > Class. Em seguida, digite o nome Docente e clique em Finish. O código-fonte desse artefato pode ser visto na Listagem 3. Nele, podemos observar anotações (vide BOX 3) do Hibernate que associam esta classe/entidade à sua respectiva tabela no banco de dados (através da anotação @Table). Como podemos observar também, cada atributo da classe é associado a um campo em sua respectiva tabela, por meio da anotação @Column. Finalizando, vamos configurar o mapeamento objeto-relacional de Docente no arquivo hibernate.cfg .xml , para habilitar o Hibernate a persistir os objetos no banco de dados, o que pode ser feito com o código da Listagem 4. BOX 3. Anotações Anotações são recursos para a declaração de metadados – dados que descrevem outros dados – úteis para localizar dependências, configurações ou para fazer verificações lógicas. Essas definições serão, então, interpretadas pelo compilador para realizar uma determinada tarefa. Figura 6. Representação do fluxo de nosso projeto-exemplo Podemos ver que entre os estados temos as transições “novo”, “excluir”, “cancelar” e “inserir”. Isso significa que da página inicial – a listagem de docentes – temos eventos que levam o usuário para incluir novo docente ou excluir docentes, ambos por meio de cliques em links. Se escolher incluir um docente, o usuário será direcionado para o formulário de cadastro. E se escolher excluir um docente, o usuário é mantido na página com a listagem de docentes após a operação. Uma vez na página do formulário de docentes o usuário pode optar por inserir o docente ou cancelar a operação. Se optar por inserir, após a operação de cadastro ser realizada com sucesso o usuário é direcionado de volta à listagem. Se escolher ca ncelar, é direcionado para uma página de resposta. Implementação do projeto exemplo Nas seções anteriores descrevemos todos os conceitos necessários à construção do nosso projeto com o SWF. A partir de agora, partiremos para a implementação da aplicação exemplo, começan- Com o JavaBean da camada de modelo criado, podemos implementar a camada de persistência, que consiste em uma interface (DocenteDAO) e sua implementação ( DocenteDAOImpl). A interface DocenteDAO declara os métodos para inserir e excluir registros do tipo Docente no banco de dados ( adicionarDocente() e removerDocente() , respectivamente), bem como para recuperá-los em uma operação de listagem ( listarDocentes()). Como a classe DocenteDAOImpl implementa a interface DocenteDAO , vemos em seu corpo a implementação dos métodos que realizam as operações com o banco de dados. Nesses métodos o Hibernate é chamado para realizar as operações de inserção (por meio do método save()), exclusão (com o método delete()), e listagem (através do método list()), como mostra o código-fonte visto nas Listagens 5 e 6.  As camadas de serviço e controle Após implementarmos o modelo e a persistência vamos codificar uma pequena camada de serviço para fazer com que a aplicação fique “magra” e possibilite que as regras de negócio sejam encapsuladas em camadas mais simples e reusáveis. Isso ocorre na medida em que os serviços assumem a responsabilidade de fazer as chamadas à camada de persistência, deixando assim a lógica de negócio encapsulada nos controllers. No nosso exemplo implementaremos a camada de serviço com uma simplificação do padrão Service Layer (BOX 4). Para isso, crie dois arquivos no pacote br.com.devmedia.gestaoacademicaweb. service: a interface DocenteService e sua implementação, a classe DocenteServiceImpl . Vemos, então, nos métodos adicionarDocente() , removerDocente() e listar() , chamadas às respectivas Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 25 Aprenda a utilizar o Spring Web Flow Listagem 3 . Código-fonte da classe Docente. package br.com.devmedia.gestaoacademicaweb.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name=”DOCENTES”) public class Docente { @Id   @Column(name=”ID”)   @GeneratedValue private Integer id; operações na camada de persistência, como demonstram as Listagens 7 e  8 , e vemos também que a classe é declarada com @Service , anotação do Spring utilizada para descrevê-la como parte da camada de serviço da aplicação. Por sua vez, os dados a serem persistidos devem ser recebidos na camada de controle, responsável por dar o tratamento correspondente às requisições de acordo com as regras de negócio definidas. Vamos então criar a camada de controle no pacote control , começando pela classe DocenteController , cujo código pode ser visto na Listagem 9. BOX 4. Padrão Service Layer Service Layer é um padrão de projeto orientado a objetos que visa organizar a lógica de negócio em serviços que, devidamente categorizados, compartilham funcionalidades. Dessa forma, o esforço conceitual de manutenção e gerenciamento destas é reduzido, uma vez que os módulos da aplicação passam a compartilhar funcionalidades comuns encapsuladas nos serviços. @Column(name=”NOME”) private String nome; @Column(name=”MATRICULA”) private String matricula; Listagem 5 . Código-fonte da interface DocenteDAO. @Column(name=”TITULACAO”) private String titulacao; package br.com.devmedia.gestaoacademicaweb.dao; import java.util.List; import br.com.devmedia.gestaoacademicaweb.model.Docente; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getMatricula() { return matricula; } public void setMatricula(String matricula) { this.matricula = matricula; } public String getTitulacao() { return titulacao; } public void setTitulacao(String titulacao) { this.titulacao = titulacao; } public interface DocenteDAO { public void adicionarDocente(Docente docente); public void removerDocente(int id); public List listarDocentes(); } Listagem 6 . Código-fonte da classe DocenteDAOImpl. package br.com.devmedia.gestaoacademicaweb.dao; import java.util.List; import br.com.devmedia.gestaoacademicaweb.model.Docente; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @Repository public class DocenteDAOImpl implements DocenteDAO {   @Autowired private SessionFactory sessionFactory; } Listagem 4 . Código-fonte do arquivo hibernate.cfg.xml, incluindo o mapeamento objeto-relacional da classe Docente.       26 public void adicionarDocente(Docente docente) {   sessionFactory.getCurrentSession().save(docente); } public void removerDocente(int id) { Docente docente = (Docente) sessionFactory.getCurrentSession() .load(Docente.class, id); if (null != docente) {   sessionFactory.getCurrentSession().delete(docente); } } public List listarDocentes() { return sessionFactory.getCurrentSession().createQuery(“from Docente”).list(); } } } Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia Listagem 7 . Código-fonte da interface DocenteService. package br.com.devmedia.gestaoacademicaweb.service; import java.util.List; import br.com.devmedia.gestaoacademicaweb.Docente; public interface DocenteService { public void adicionarDocente(Docente docente); public void removerDocente(int id); public List listarDocentes(); } Listagem 8 . Código-fonte da implementação do serviço DocenteServiceImpl. package br.com.devmedia.gestaoacademicaweb.service; import java.util.List; import br.com.devmedia.gestaoacademicaweb.Docente; import br.com.devmedia.gestaoacademicaweb.dao.DocenteDAO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class DocenteServiceImpl implements DocenteService {   @Autowired private DocenteDAO docenteDAO; @Transactional public void adicionarDocente(Docente docente) {   docenteDAO.adicionarDocente(docente); }   @Transactional public void removerDocente(int id) {   docenteDAO.removerDocente(id); }   @Transactional public List listarDocentes() { return docenteDAO.listarDocentes(); } } Listagem 9 . Código-fonte da classe DocenteController. package br.com.devmedia.gestaoacademicaweb.control; import java.util.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import br.com.devmedia.gestaoacademicaweb.model.Docente; import br.com.devmedia.gestaoacademicaweb.service.DocenteService; @Controller public class DocenteController {   @Autowired private DocenteService docenteService; public List listarDocentes() { List docentes = docenteService.listarDocentes(); return docentes; } public void adicionarDocente(Docente docente) { docenteService.adicionarDocente(docente); } public void removerDocente(int id) { docenteService.removerDocente(id); } } Observe nessa listagem que os métodos acessam a camada de serviço para realizar as operações com o banco de dados, como mostra a instrução docenteService.adicionarDocente(docente). Implementando o fluxo com o Spring Web Flow Com as camadas de modelo, controle, serviço e persistência concluídas, já é possível implementar o f luxo da aplicação, bem como a camada de visão, que conterá as páginas que corresponderão aos estados declarados no f luxo. Para manter o código estruturado – além de permitir u m eventual reuso dos fluxos criados – recomendamos a criação de um arquivo XML para cada fluxo da aplicação. No nosso caso armazenaremos estes arquivos XML dentro de WEB-INF/flows. Dito isso, dentro do diretório  flows crie o arquivo inserirDocente .xml , o que é feito clicando com o botão direito sobre este diretório e selecionando New > File. Uma vez que é necessário realizar as configurações correspondentes ao SWF no arquivo spring-context. xml , ainda não alteraremos o XML recém-criado. Vamos, agora, modificar as configurações do Spring alterando seu arquivo de configuração para que fique conforme demonstrado na Listagem 10. As principais modificações que fizemos em spring-context. xml  para configurar o SWF se referem ao mapeamento dos fluxos que nosso website possui. No nosso caso chamamos o fluxo de inserirDocente. Podemos verificar sua descrição no nó  , onde há a declaração  , que especif ica que o fluxo identificado por inserirDocente  está declarado no arquivo de mesmo nome, com extensão XML, localizado em  /WEB-I NF/flows/ . Seguindo em frente, declaramos os controllers da nossa aplicação utilizando a tag  , como verificado no trecho . Vale lembrar que no nosso exemplo temos apenas um controller , que será identificado pelo nome docenteController. No mais, foram incluídas em spring-context.xml as configurações padrão do SWF, especialmente das classes FlowHandlerMapping e FlowHandlerAdapter , os chamados  flow handlers   do Spring Web Flow. Desenvolvendo as páginas da camada de visão Antes de iniciar o desenvolvimento do fluxo, vamos criar as páginas da nossa camada de visão: um formulário para cadastro de Docentes (inserir_docente_form.jsp ); uma página que exibirá uma lista dos docentes cadastrados ( listar_docente.jsp); e a página de resposta ao cancelamento (cancelado.jsp). Todas serão armazenadas no diretório WEB-INF/views. Dito isso, com o botão direito sobre o diretório WEB-INF/views , selecione New > JSP File . Na janela de propriedades do arquivo, digite no campo File Name o nome “inserir_docente_form.jsp” e clique em Finish . O código-fonte do formulário de cadastro de docentes pode ser visto na Listagem 11 , enquanto é possível visualizar sua aparência na  Figura 7. Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 27 Aprenda a utilizar o Spring Web Flow Listagem 10 . Código-fonte do arquivo spring-context.xml, incluindo as configurações do SWF. ...                   classpath:hibernate.cfg.xml     ${jdbc.dialect} true         Listagem 11. Código-fonte do formulário de cadastro de docentes. <%@taglib uri=”http://www.springframework.org/tags”  prefix=”spring” %> <%@taglib uri=”http://www.springframework.org/tags/form”  prefix=”form” %> Cadastro de Docentes

Formulário de Cadastro de Docentes

                       
Nome:
Matrícula:
Titulação:
        28 Figura 7. Formulário de cadastro de docentes Para construir esse formulário fizemos uso dos pacotes de taglibs do Spring; em especial, do pacote form , como é mostrado nos trechos de código iniciados por . Essa taglib é utilizada para criar os campos do formulário conforme verificado em . Já a associação entre os formulários e as respectivas classes na camada de modelo é feita por meio da propriedade modelAttribute na tag 
 , como vemos em . Nesse trecho constatamos que o formulário de cadastro de do- Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia cente é associado à classe da camada de modelo identificada por docenteModel. Essa identificação é configurada no arquivo do fluxo, e a veremos mais adiante, quando apresentarmos o conteúdo do arquivo inserirDocente.xml. Nesse caso, a propriedade path é usada nas declarações dos campos para indicar a que atributo da classe cada campo de formulário será associado. A passagem de uma página para outra – que o SWF identifica como transição entre estados – é feita por meio do disparo de eventos pelo usuário, ou seja, quando este clica em um botão, ou num link  , por exemplo. Com isso, cada evento disponível nas páginas deve ser identificado (na sua propriedade name) pelo prefixo _eventId na declaração do elemento de página que foi construído para dispará-lo. Por exemplo, o botão Salvar no formulário de cadastro de docentes é identificado por _ eventId_inserir , como pode ser visto no trecho . Agora vamos criar os arquivos cancelado.jsp e listar_docentes.  jsp , que representam, respectivamente, a página de resposta de cancelamento e a listagem dos docentes cadastrados. O código da página de resposta de cancelamento é bastante simples e é apresentado na Listagem 12. Já o código-fonte da listagem de docentes é demonstrado Listagem 13 , enquanto sua aparência pode ser vista na Figura 8. Listagem 12. Código-fonte da página de cancelamento. <%@ page language=”java”  contentType=”text/html; charset=UTF-8”    pageEncoding=”UTF-8” %> Insert title here

Processo de cadastramento cancelado

Listagem 13. Código-fonte da listagem de docentes. <%@taglib uri=”http://java.sun.com/jsp/jstl/core”  prefix=”c” %> Listagem de Docentes

Docentes Cadastrados

+ Novo Docente

               
NomeMatrículaTitulação 
${docente.nome} ${docente.matricula}${docente.titulacao} Excluir
Figura 8. Listagem de Docentes No código dessa página os eventos disponíveis são cliques nos links para “novo docente” e “excluir”. Aqui é importante lembrar que os eventos devem ser identificados pelo prefixo _eventId , e isso é implementado na declaração dos links, como em . Note também que incluímos no link o fluxo a que esse evento se refere (no nosso caso, inserirDocente), por meio do parâmetro flowExecutionUrl. O restante da página utiliza taglibs JSTL para percorrer uma coleção e exibir os registros de docente cadastrados no banco – devidamente recuperados pelas demais camadas da aplicação. Agora que as páginas foram criadas podemos fazer a implementação do fluxo da aplicação modificando o arquivo inserirDocente. xml , como demonstrado na Listagem 14. O primeiro elemento que devemos notar nesse arquivo é a declaração da variável docenteModel , feita no trecho . Essa variável é utilizada para descrever a associação entre a classe Docente na camada de modelo e o formulário de cadastro de docentes. Como demonstrado nessa listagem, os estados do fluxo são descritos em nós XML com o sufixo -state. Visto que nosso pro jeto possui dois view-states (listagem de docentes e formulário de cadastro de docentes) e um end-state  (operação cancelada), Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 29 Aprenda a utilizar o Spring Web Flow utilizamos os nós e   para declarar esses estados. Por sua vez, especificamos o handler do formulário de cadastro de docentes com o nó  , no trecho  , e definimos as transições em nós . A propriedade on de uma transição indica em que evento ela deve ser disparada; enquanto a propriedade to , para qual estado o usuário é direcionado. Por exemplo, na marcação  , uma vez disparado o evento cancelar o usuário será encamin hado para o estado cancelado. Listagem 14. Código-fonte do fluxo inserirDocente.xml.                 Para o evento de submissão do formulário, declaramos uma transição como . Nesse trecho fazemos com que o action-state inserirDocenteAction seja acionado quando o evento inserir for disparado (podemos ver na Listagem 11 que esse evento está assoc iado ao clique no botão Salvar). Já a operação adicionarDocente() , da classe DocenteController , será realizada durante o action-state inserirDocenteAction , o que viabilizará a inserção dos dados do formulário de cadastro de docentes no banco. 30 Para isso, especificamos o nó  , como mostra o trecho . Esse recurso é utilizado novamente na listagem de docentes, conforme a marcação  , que faz com que o método listarDocentes() seja executado na renderização da listagem de docentes. Nesse segundo exemplo vemos outro recurso do SWF ser utilizado: o retorno do método é armazenado em uma variável que pode ser acessada na camada de visão. A propriedade result da tag  define o armazenamento do retorno do método listarDocentes()   em uma variável chamada docenteList. Nesse trecho, note que tamb ém é indicado o escopo da variável (viewScope , que significa que a variável estará disponível apenas no estado em que ela foi declarada; no caso, a view-state listar). Outros escopos existem, mas não os exploramos nesse ar tigo.  Com isso, a última coisa que falta para nosso projeto ser concluído é criar o banco de dados. Para tanto, vamos usar o script de criaçã o da tabela descrito na Listagem 15. Com isso nosso exemplo está pronto para ser testado, o que pode ser feito após iniciar o Tomcat e digitar, no navegador, a URL http://localhost:8080/inserirDocente. Listagem 15. Script de criação da tabela DOCENTES. CREATE TABLE DOCENTES ( ID INT PRIMARY KEY AUTO_INCREMENT, NOME VARCHAR(100), MATRICULA VARCHAR(100),  TITULACAO VARCHAR(100) ); Ivar Jacobson, Grady Booch e James Rumbaugh, que podemos chamar de pais da Unified Modeling Language  (UML), afirmam que um “estado” é o momento durante o ciclo de vida de um objeto no qual ele satisfaz algumas condições, executa algumas atividades ou espera por eventos. Nesse art igo, vemos como esse conceito é adotado para descrever a navegação do usuár io, mais especificamente, as páginas que ele pode visitar e os eventos que dispara pelo caminho. Esse conceito tornou possível o encapsulamento do f luxo, diminuindo a escrita de código, deixando a estrutura de navegação do website mais clara e, eventualmente, permitindo que os fluxos sejam reutilizados não só em outras partes do próprio site , mas em outras aplicações. Vale destacar que o código, especialmente das camadas de controle e visão, está mais simples do que o construído usando outros frameworks MVC, como o Struts, ou o próprio Spring, na medida em que menos código precisou ser escrito. Também é importante ressaltar que o SWF é perfeitamente integrável a outros frameworks MVC. No entanto, sua integração com o Spring MVC é mais interessante, até pela obviedade de fazer parte do mesmo projeto, assim como pelo mecanismo de injeção de dependência do Spring, algo bastante celebrado na comunidade de desenvolvedores. Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia No que diz respeito à estrutura do projeto, o fato dos fluxos serem organizados em um arquivo XML e identificados de forma única constitui mais uma vantagem do SWF, especialmente por deixar a estrutura da aplicação visível e viabilizar o reuso. Outra vantagem é a possibilidade de organizar os fluxos em subfluxos de maneira transparente, separando toda a lógica de negócio da lógica de navegação, uma inovação no que diz respeito ao padrão Model-View-Controller. Sendo assim, os recursos do SWF devem ser explorados de forma bastante ampla para obter um melhor gerenciamento do workflow na plataforma web, o que nos possibilita afirmar que o SWF é ideal para construir aplicações web em que as regras de negócio demandam guiar o usuário por fluxos bem controlados, estruturados e, ocasionalmente, reusáveis. Links: Site oficial do projeto Spring Web Flow. Autor http://projects.spring.io/spring-webflow/  Alessandro Jatobá  [email protected] Site oficial do projeto Spring. Mestre em Ciência da Computação pela UFRJ, Doutorando em Engenharia também pela UFRJ com Doutorado-Sanduíche na https://spring.io/  Universidade de Waterloo, no Canadá. Trabalha com Java há quase 20 Site oficial do Hibernate. http://www.hibernate.org anos, a maior parte deles liderando equipes de desenvolvedores em projetos Web. Também é professor universitário, lecionando temas ligados ao desenvolvimento orientado a objetos. Site oficial do Apache Maven. https://maven.apache.org/  Copyright - Proibido copiar ou distribuir. Todos os direitos reservados para DevMedia 31