Padrão de Projeto View Helper em Java e seus paradoxos

Veja neste artigo como funciona o Padrão de Projeto View Helper no Java e os problemas encontrados ao entender tal conceito.

O padrão de projeto View Helper tem como principal objetivo formatar os dados ou realizar qualquer tipo de tratamento antes que estes sejam enviados ao Model, em se tratando se uma aplicação MVC.

Suponha, por exemplo, que desejamos validar CPF's antes de salvar, e este validador de CPF será utilizado em toda nossa aplicação. Neste caso o mais apropriado é centralizar o código em vez de ficar criando vários métodos, em várias partes do projeto, apenas para validar o CPF. Se tratando de uma aplicação MVC, poderíamos validar esse CPF antes mesmo de enviar para o Model, ainda no View, através do padrão de projeto View Helper.

A sua utilização e organização é simples. Este geralmente é uma Classe com o final do nome “Helper” que possui métodos estáticos, ou seja, não precisaremos instanciar, e nem deveríamos, uma classe Helper. Para melhor organização, é ideal criarmos um pacote chamado “helper” apenas para guardar esses tipos de classes. Isso deve ser uma boa prática para qualquer outro projeto, criar um pacote distinto para cada camada da sua aplicação: DAO, POJO, ManagedBean e etc.

Figura 1. Diagrama de Classes do View Helper

Na figura acima podemos ver onde está a nossa Classe Helper, entre a View e o Model. Ela é responsável por “adaptar” nosso dado para o Model, mas não só isso, a inúmeras utilidades para este padrão, que não se restringe em apenas “adaptar” o dado e enviar para o Model, na verdade você pode usar essa classe para colocar métodos que possam auxiliar a View em sua tarefa.

Nos próximos tópicos veremos uma exemplos em Java utilizando o recurso dos Helpers para auxiliar a View. Lembrando que por se tratar de um exemplo didático, não construiremos o Model da aplicação, pois este não nos interessa no momento. Caso você veja a necessidade de possuir uma instância do Helper na memória, você poderá utilizar o padrão de projeto Singleton para assegurar que haverá apenas 1 instância de determinado Helper na memória.

Projeto com Helper

Vamos criar aqui uma classe Helper de exemplo que fará tarefas referentes ao IP da máquina. Primeiro vamos mostrar a classe e depois pensar nas utilidades que esta pode ter:

package br.com.myapp.helper; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.swing.JOptionPane; public class IPHelper { /* * Testa se um ip está na rede ou não */ public static boolean isOnLine(String host) { boolean ping = false; if (isIpValido(host)) { String comando = "ping -c 1 " + host; try { String s = ""; Process process = Runtime.getRuntime().exec(comando); BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); int c = 0; while ((s = stdInput.readLine()) != null){ if (c == 1){ String outProcess = s.toString(); if (outProcess.contains("64 bytes") || outProcess.contains("40 bytes")) { ping = true; } else { ping = false; } } c++; } } catch (IOException ex) { ex.printStackTrace(); } } else { JOptionPane.showMessageDialog(null,"Endereço Ip Inválido: " + host, "Validação do IP", JOptionPane.WARNING_MESSAGE); } return ping; } /* * Realiza um ping dado determinado IP */ public static String ping(String ip){ String resposta=""; String comando="ping -c 1 "+ip; try { Scanner s = new Scanner( Runtime.getRuntime().exec(comando).getInputStream()); while(s.hasNextLine()) { resposta+=s.nextLine()+"\n"; } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return resposta; } /* * Cria uma comunicacao com a porta desejada, se a porta estiver * disponivel returna true, caso contrário uma exception ira ocorrer * e retornara false */ public static boolean criarComunicacaoComPorta(int port) { try { ServerSocket srv = new ServerSocket(port); srv.close(); srv = null; return true; } catch (IOException e) { return false; } } /* * Verifica se o IP é valido */ public static boolean isIpValido(String ip) { if (ip == null || ip.isEmpty()) return false; ip = ip.trim(); if ((ip.length() < 6) & (ip.length() > 15)) return false; try { Pattern pattern = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]? [0-9][0-9]?)\\.)(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"); Matcher matcher = pattern.matcher(ip); return matcher.matches(); } catch (PatternSyntaxException ex) { return false; } } }
Listagem 1. IPHelper

Vamos pensar agora nas utilidades envolvidas em uma Classe como a descrita acima. Em um projeto de grande porte, podem haver diversos módulos nos quais seja necessário realizar checagem se um servidor está respondendo antes de realizar qualquer tarefa remota. Além disso podemos mostrar o IP do usuário do rodapé do sistema, com a função demonstrada acima, função essa muito útil e utilizada na maioria dos sistema que funcionam em Rede. Perceba que a nossa classe acima possui todos os métodos estáticos, isso nos permite chamar qualquer um dos métodos diretamente sem precisar instanciar a classe, ou seja, criar um objeto IPHelper.

Como nosso Helper será utilizado várias vezes em todo o sistema, é importante fazer esse gerenciamento de instanciações de forma correta, em outras palavras, se você defini que o seu Helper deverá ser instanciado toda vez que for necessário utilizá-lo, você pode estar criando um gargalo no seu sistema, pois em casos onde o Helper é utilizado diversas vezes, dentro de laços (for, while, do while etc), por exemplo, serão criadas instancias inúmeras desse objeto na memória.

Não se confunda, o Helper não deve ter lógica de negócio, pois esse não é seu objetivo, para isso existem outras camadas (dependendo da arquitetura que você adotou), como dissemos anteriormente, o Helper é um auxiliar à View, ajudando a mesma a realizar conversões, validações e checagens antes de enviar os dados à camada responsável pela lógica de negócio.

Pense na seguinte situação: Você está construindo um Sistema de Transporte Aéreo, você precisa definir todos os aviões e seus respectivos comandantes, mas ao realizar o cadastro destes comandantes você tem em mente a seguinte regra de negócio: “Uma pessoa só pode ser comandante se tiver o Brevê (Carteira de Habilitação para Pilotar Aviões)”. Sendo assim, ao cadastrar um avião você vai precisar escolher o comandante vinculado a ele, então onde devemos filtrar as pessoas que poderão ser vinculadas ao avião, sendo que só poderão ser as pessoas que possuem brevê ? A resposta é simples: Essa definição deverá ser feita na sua regra de negócio, o retorno de dados já deve estar filtrado com as pessoas habilitadas para comandar aviões, a View e o Helper nada farão em relação a isso, na verdade a View e o Helper nem sabem da existência desta regra, e deve continuar assim.

A vantagem dessa divisão tão metódica e a portabilidade que o sistema vai nos proporcionar, pois poderíamos a qualquer momento mudar um sistema Web para Desktop e vice-versa, sem prejudicar o funcionamento da lógica do sistema. O Helper é um caso a parte nesta portabilidade, pois a migração de plataformas (web ↔ desktop) de um Helper vai depender do nível de acoplamento deste com a View. Veja o nosso Helper da listagem 1 por exemplo, você acha que ele poderia ser migrado sem problemas de um sistema desktop para web ? A resposta é não totalmente, pois ele possui “JoptionPanes”, recurso que pode não estar presente em outras plataformas que desejamos migrar.

Helper ou Util

Há muitos desenvolvedores que se confundem, na diferença entre uma Classe Helper e uma Util. De fato são duas classes com princípios muito parecidos: Auxiliar em algo, aumentar a reusabilidade de código e diminuir redundâncias. Podemos dividir assim:

Nada impede que você use Utils como Helpers e Helpers como Utils, mas você estaria fugindo do padrão e consequentemente outros desenvolvedores acostumados com esse padrão achariam seu código confuso e desorganizado.

Conclusão

Este artigo teve como objetivo não só mostrar o uso do Padrão de Projeto View Helper, mas também mostrar os principais problemas encontrados e ideias que confundem alguém que está iniciando e se depara com uma Classe Util e uma Classe Helper, por exemplo, pensando que ambas tem a mesma função.

É importante salientar que as práticas descritas neste artigo são teóricas e massivamente utilizadas no mercado, caso você utilize essas classes (Helper e Util) de outra forma, sinta-se a vontade para opinar e fazer sua sugestão a uma forma melhor de se trabalhar.

Artigos relacionados