segunda-feira, 7 de dezembro de 2015

Um DAO totalmente genérico e independente

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

Hoje iremos ousar, hoje iremos ostentar no camarote. Se você lembrou da imagem abaixo, sim, você está certo.
Figura 1 - O rei do camarote
Seu sobrenome será "ostentação" mas, estamos aqui falando da ostentação do código-fonte. Eu afirmo com toda certeza que somente com Java e uma IDE, conseguimos criar uma camada DAO com um alto índice de código reutilizável. Contudo, a pergunta é: como ?

Se você é novo por aqui, bote seu email na caixa de texto para se inscrever no blog (clique aqui para ver como) ou curta a página do facebook, o link está no fim do post.

Voltando.....

Primeiro, precisamos entender como funciona um método que está na camada DAO. Se você não sabe o que é DAO, clique aqui ou dê uma olhada nas referências.
Figura 2 - O funcionamento de um método da camada DAO
É possível notar que algumas etapas sempre se repetem para todos os métodos DAO. Essas etapas são:
  • Abertura de conexão.
  • Configuração do commit automático.
  • Criação do statement.
  • Passagem de parâmetros para statement.
  • Execução do statement.
  • Commit da transação.
  • Rollback da transação (em caso de erro).
  • Encerramento da conexão.

E o que não se repete ? O que é diferente de um método para o outro ?
  • Query SQL
  • Parâmetros
Se pararmos para refletir sobre isso, realmente o que muda de um método para o outro é a query SQL realizada e os parâmetros que são passados. Então, temos muito código repetido o qual, podemos eliminar. Caso contrário, teremos problemas muito maiores no futuro. Mas, como eliminar ?

A resposta é simples. Temos que ter um bom projeto orientado a objetos onde, nossa meta é a reusabilidade de código.

----------------------------------------------------------------------------------------------------------

AVISO: É necessário o conhecimento dos seguintes assuntos para que, tenhamos um total aproveitamento do assunto:
  • Java
  • Orientação a objetos
  • Java + JDBC
  • SQL
  • UML
----------------------------------------------------------------------------------------------------------

Vamos modelar nossa solução.

Figura 3 - Diagrama de classes
GenericDAO

Essa superclasse possui os métodos que realizam as operações de banco (insert, update, delete, select) de forma genérica, configura os parâmetros da query, abrem conexão e encerram conexão. Como existem muitos métodos nessa classe, farei comentários pontuais sobre alguns.
  • insertUpdateDelete - Método responsável pelas operações de escrita (insert, update e delete). Consegue desempenhar a função genérica porque recebe como parâmetros, a query e os parâmetros da query e porque utiliza o método receiveParameters o qual, realiza a configuração dos parâmetros no objeto Statement. O índice dos parâmetros é conservado pela sua posição na lista.
  • receiveParameters - Método responsável pela configuração dos parâmetros da query recebidos no objeto Statement. Essa configuração é feita através de uma estrutura condicional que verifica o tipo da instância que está na lista de parâmetros. Uma vez que é determinado o tipo, o método correto do objeto Statement pode ser invocado. O índice dos parâmetros é conservado pela sua posição na lista.
  • findAll - Método responsável por listar registros de acordo com a query passada. O mapeamento dos dados do objeto ResultSet em objetos do tipo Funcionario acontece dentro da iteração do primeiro objeto e é feito através da implementação do método mapping.
  • findById - Método responsável por listar somente 1 registro de acordo com a query passada. Assim como no método acima, o mapeamento também é feito através do método mapping. Contudo, é necessário retornar sempre o primeiro registro portanto, uma verificação de cursor é feita em cima do retorno da query. Caso a query não tenha retornado nada, o objeto ResultSet se torna nulo e o método retorna um objeto zerado. Caso contrário, o cursor é movido em 1 posição e os dados são obtidos.
 public void insertUpdateDelete(String sql, List<Object> parametros){  
     Connection connection = null;  
     PreparedStatement preparedStatement = null;  
     try {  
       connection = this.openConnection();  
       connection.setAutoCommit(false);  
       preparedStatement = connection.prepareStatement(sql);  
       this.receiveParameters(preparedStatement, parametros);  
       preparedStatement.execute();  
       connection.commit();  
     } catch (SQLException ex) {  
       ex.printStackTrace();  
       if(connection != null){  
         try {  
           connection.rollback();  
         } catch (SQLException sqle) {  
           sqle.printStackTrace();  
         }  
       }  
     } finally {  
       this.closeConnection(connection, preparedStatement);  
     }  
   }  

   private void receiveParameters(PreparedStatement preparedStatement, List<Object> parametros) throws SQLException{  
     int paramPos = 1;  
     for(Object parametro : parametros){  
       if(parametro == null){  
         preparedStatement.setNull(paramPos, 1);  
       } else if(parametro instanceof Integer){  
         preparedStatement.setInt(paramPos, (Integer) parametro);  
       } else if(parametro instanceof Long){  
         preparedStatement.setLong(paramPos, (Long) parametro);  
       } else if(parametro instanceof Float){  
         preparedStatement.setFloat(paramPos, (Float) parametro);  
       } else if(parametro instanceof Double){  
         preparedStatement.setDouble(paramPos, (Double) parametro);  
       } else if(parametro instanceof Date){  
         preparedStatement.setDate(paramPos, new java.sql.Date(((Date) parametro).getTime()));  
       } else if(parametro instanceof String){  
         preparedStatement.setString(paramPos, parametro.toString());  
       }  
       paramPos++;  
     }  
   }  

   public List findAll(String sql, List<Object> parametros, RowMapping mapping){  
     Connection connection = null;  
     PreparedStatement preparedStatement = null;  
     ResultSet resultSet = null;  
     List rows = new ArrayList();  
     try {  
       connection = this.openConnection();  
       connection.setAutoCommit(false);  
       preparedStatement = connection.prepareStatement(sql);  
       this.receiveParameters(preparedStatement, parametros);  
       resultSet = preparedStatement.executeQuery();  
       while(resultSet.next()){  
         rows.add(mapping.mapping(resultSet));  
       }        
       connection.commit();  
     } catch (SQLException ex) {  
       ex.printStackTrace();  
       if(connection != null){  
         try {  
           connection.rollback();  
         } catch (SQLException sqle) {  
           sqle.printStackTrace();  
         }  
       }  
     } finally {  
       this.closeConnection(connection, preparedStatement, resultSet);  
     }  
     return rows;  
   }  

   public Object findById(String sql, List<Object> parametros, RowMapping mapping){  
     Connection connection = null;  
     PreparedStatement preparedStatement = null;  
     ResultSet resultSet = null;  
     Object row = null;  
     try {  
       connection = this.openConnection();  
       connection.setAutoCommit(false);  
       preparedStatement = connection.prepareStatement(sql);  
       this.receiveParameters(preparedStatement, parametros);  
       resultSet = preparedStatement.executeQuery();  
       resultSet = resultSet.isBeforeFirst() ? resultSet : null;  
       if(resultSet != null){  
         resultSet.next();  
       }  
       row = mapping.mapping(resultSet);  
       connection.commit();  
     } catch (SQLException ex) {  
       ex.printStackTrace();  
       if(connection != null){  
         try {  
           connection.rollback();  
         } catch (SQLException sqle) {  
           sqle.printStackTrace();  
         }  
       }  
     } finally {  
       this.closeConnection(connection, preparedStatement, resultSet);  
     }  
     return row;  
   }  
 }  

FuncionarioDAO

Esta é uma subclasse de GenericDAO. Utiliza os métodos da classe pai para persistir ou recuperar dados do banco de dados relacional. Os métodos de escrita são bem simples, englobam somente a criação da query e a adição dos parâmetros na lista. Os métodos que recuperam dados utilizam um recurso muito prático do Java, a implementação de interfaces em métodos. Dessa forma, eu não preciso criar uma classe nova a qual, implementa RowMapping. Eu posso passar diretamente a implementação da interface como um parâmetro do método. O método definido na interface RowMapping é o responsável pelo mapeamento ResultSet - Funcionario.

   public void insert(Funcionario funcionario){  
     String sql = "INSERT INTO funcionario ("  
         + "NM_FUNCIONARIO, "  
         + "EM_FUNCIONARIO,"  
         + "DT_NASCIMENTO_FUNCIONARIO,"  
         + "MAT_FUNCIONARIO,"  
         + "NM_LOGRADOURO,"  
         + "NUM_LOGRADOURO,"  
         + "NM_BAIRRO"  
         + ") VALUES (?,?,?,?,?,?,?)";  
     List<Object> parametros = new ArrayList<>();  
     parametros.add(funcionario.getNome());  
     parametros.add(funcionario.getEmail());  
     parametros.add(funcionario.getDataNascimento());  
     parametros.add(funcionario.getMatricula());  
     parametros.add(funcionario.getLogradouro());  
     parametros.add(funcionario.getNumero());  
     parametros.add(funcionario.getBairro());  
     super.insertUpdateDelete(sql, parametros);  
   }  
   
   public List<Funcionario> findAll(){  
     String sql = "SELECT * FROM FUNCIONARIO";  
     List<Object> parametros = new ArrayList<>();  
     return super.findAll(sql, parametros, new RowMapping<Funcionario>() {  
       @Override  
       public Funcionario mapping(ResultSet resultSet) throws SQLException{  
         Funcionario funcionario = new Funcionario();  
         if(resultSet != null){  
           funcionario.setId(resultSet.getLong("ID"));  
           funcionario.setNome(resultSet.getString("NM_FUNCIONARIO"));  
           funcionario.setEmail(resultSet.getString("EM_FUNCIONARIO"));  
           funcionario.setDataNascimento(resultSet.getDate("DT_NASCIMENTO_FUNCIONARIO"));  
           funcionario.setMatricula(resultSet.getString("MAT_FUNCIONARIO"));  
           funcionario.setLogradouro(resultSet.getString("NM_LOGRADOURO"));  
           funcionario.setNumero(resultSet.getInt("NUM_LOGRADOURO"));  
           funcionario.setBairro(resultSet.getString("NM_BAIRRO"));  
         }  
         return funcionario;  
       }  
     });  
   }  
     
   public Funcionario findById(Long id){  
     String sql = "SELECT * FROM FUNCIONARIO WHERE ID = ?";  
     List<Object> parametros = new ArrayList<>();  
     parametros.add(id);  
     return (Funcionario) super.findById(sql, parametros, new RowMapping<Funcionario>() {  
       @Override  
       public Funcionario mapping(ResultSet resultSet) throws SQLException{  
         Funcionario funcionario = new Funcionario();  
         if(resultSet != null){  
           funcionario.setId(resultSet.getLong("ID"));  
           funcionario.setNome(resultSet.getString("NM_FUNCIONARIO"));  
           funcionario.setEmail(resultSet.getString("EM_FUNCIONARIO"));  
           funcionario.setDataNascimento(resultSet.getDate("DT_NASCIMENTO_FUNCIONARIO"));  
           funcionario.setMatricula(resultSet.getString("MAT_FUNCIONARIO"));  
           funcionario.setLogradouro(resultSet.getString("NM_LOGRADOURO"));  
           funcionario.setNumero(resultSet.getInt("NUM_LOGRADOURO"));  
           funcionario.setBairro(resultSet.getString("NM_BAIRRO"));  
         }  
         return funcionario;  
       }  
     });  
   }  
 }  

RowMapping

Interface que possui único método, mapping. Este método é responsável pelo mapeamento do objeto ResultSet em uma entidade do sistema. Pelo fato de sempre retornar uma nova instância sendo essa, qualquer objeto do sistema, a interface é marcada como genérica . Portanto, o tipo de retorno também é genérico, ou seja, T.

Como citado anteriormente, a implementação dessa interface acontece na passagem de parâmetros dos métodos os quais, esperam por uma implementação da mesma.

 public interface RowMapping<T> {  
     
   /**  
    * Método que realiza o mapeamento do result set em um objeto.  
    * @param resultSet Objeto do tipo ResultSet. Contém o retorno da query.  
    * @return T Retorna um objeto especificado pela generic.  
    * @throws SQLException Erro de acesso ao banco ou SQL.  
    */  
   T mapping(ResultSet resultSet) throws SQLException;  
 }  

Funcionario

Um POJO comum.

Para criar a tabela do banco de dados, utilize o script abaixo:

 CREATE TABLE `funcionario` (  
  `ID` int(11) NOT NULL AUTO_INCREMENT,  
  `NM_FUNCIONARIO` varchar(45) DEFAULT NULL,  
  `EM_FUNCIONARIO` varchar(45) DEFAULT NULL,  
  `DT_NASCIMENTO_FUNCIONARIO` date DEFAULT NULL,  
  `MAT_FUNCIONARIO` varchar(45) DEFAULT NULL,  
  `NM_LOGRADOURO` varchar(45) DEFAULT NULL,  
  `NUM_LOGRADOURO` int(11) DEFAULT NULL,  
  `NM_BAIRRO` varchar(45) DEFAULT NULL,  
  PRIMARY KEY (`ID`)  
 ) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8$$  

Terminamos nosso DAO reutilizável e genérico. Sinta-se a vontade para criticar, comentar ou propor melhorias. Até porque é assim que conseguimos evoluir.

Baixe o projeto
Dropbox: https://www.dropbox.com/s/epg48k36xfp1brv/GenericDAO.rar?dl=0
GitHub: https://github.com/PrecisoEstudarSempre/GenericDAO.git

Agradecimentos a Bruno Souza.

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, na nossa página do facebook ou mande um email.

E-mail: precisoestudarsempre@gmail.com

Referências:
Objeto de acesso a dados - https://pt.wikipedia.org/wiki/DAO
Leia Mais ››

domingo, 29 de novembro de 2015

Dividindo e conquistando - O algoritmo Merge Sort

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar. Hoje abordaremos mais um tópico da série: Vamos por ordem nessa bagunça ? - Algoritmos de ordenação

Clique para dar uma olhada.

Neste post aprenderemos o algoritmo de ordenação Merge Sort.

AVISO: Esse algoritmo não é para iniciantes logo, você vai precisar de algum tempo para entendê-lo. Procure a supervisão de um adulto. =)

A nossa primeira pergunta é: como ele funciona ?

Criado por John von Neumann em 1945, este algoritmo utiliza a técnica de dividir para conquistar, diferentemente do seu primo, o algoritmo Bubble Sort. Possui complexidade de tempo Θ(n log 2n), no pior caso e no melhor caso, Θ(n log n).
Figura 1 - Criador do algoritmo Merge Sort
O algoritmo Merge Sort é de fácil implementação. É conceitualmente mais fácil do que a ordenação quicksort e a ordenação Shell mas, possui uma desvantagem. Requer um vetor adicional na memória, igual em tamanho àquele sendo ordenado. Se seu vetor original mal couber na memória, a ordenação não funcionará. Porém, se você possuir bastante espaço, isso não será um problema.

Sua estratégia consiste em criar uma sequência ordenada a partir de outras duas já ordenadas. Para tal, divide-se a sequência original em pares de dados, e ordena-se. Depois, agrupa-se em sequências de quatro elementos, e assim por diante até a sequência original estar separada em apenas duas partes. Este processo se repete até o array estar totalmente unido e ordenado.

A imagem abaixo ilustra bem o explicado.

Figura 2 - Funcionamento do algoritmo Merge Sort
Interprete cada faixa da imagem como uma etapa, ou seja, na primeira etapa é quando o array está todo unido, e assim por diante. É possível notar claramente como esse algoritmo funciona. Na primeira etapa temos o array todo unido, na segunda já dividimos ele ao meio (a divisão não é exata porque o tamanho do array não é par), ou seja, já estamos utilizando da técnica dividir para conquistar e esse processo de divisão se repete até a etapa 4.

Na etapa 4 dividimos o array até o máximo, ou seja, quando sobra somente 1 elemento, esse é o nosso caso base. A partir daí, podemos começar a ordená-lo. Na etapa 5 realizamos a primeira ordenação, ou seja, a ordenação dos pares. A partir da ordenação dos pares, podemos unir eles e ordená-los novamente, Esse processo se repete até o ponto em que obtemos o array todo unido novamente só que, agora ordenado.

O gif abaixo mostra todo o processo de ordenação de forma animada.
Figura 3 - Animação de funcionamento do algoritmo Merge Sort
Mas como transformar toda essa inteligência em um programa Java ?

1:  public class MergeSort {  
2:       private int[] numbers;  
3:       private int[] helper;  
4:    
5:       private int number;  
6:    
7:       public void sort(int[] values) {  
8:            this.numbers = values;  
9:            number = values.length;  
10:            this.helper = new int[number];  
11:            mergeSort(0, number - 1);  
12:       }  
13:    
14:       private void mergeSort(int begin, int end) {  
15:            // check if begin is smaller then end, if not then the array is sorted  
16:            if (begin < end) {  
17:                 // Get the index of the element which is in the middle  
18:                 int middle = begin + (end - begin) / 2;  
19:                 // Sort the left side of the array  
20:                 mergeSort(begin, middle);  
21:                 // Sort the right side of the array  
22:                 mergeSort(middle + 1, end);  
23:                 // Combine them both  
24:                 merge(begin, middle, end);  
25:            }  
26:       }  
27:    
28:       private void merge(int begin, int middle, int end) {  
29:    
30:            // Copy both parts into the helper array  
31:            for (int i = begin; i <= end; i++) {  
32:                 helper[i] = numbers[i];  
33:            }  
34:    
35:            int i = begin;  
36:            int j = middle + 1;  
37:            int k = begin;  
38:            // Copy the smallest values from either the left or the right side back  
39:            // to the original array  
40:            while (i <= middle && j <= end) {  
41:                 if (helper[i] <= helper[j]) {  
42:                      numbers[k] = helper[i];  
43:                      i++;  
44:                 } else {  
45:                      numbers[k] = helper[j];  
46:                      j++;  
47:                 }  
48:                 k++;  
49:            }  
50:            // Copy the rest of the left side of the array into the target array  
51:            while (i <= middle) {  
52:                 numbers[k] = helper[i];  
53:                 k++;  
54:                 i++;  
55:            }  
56:       }  
57:  }  

Pronto, conseguimos !! Vamos entender agora como o programa Java implementa o algoritmo.

Classe MergeSort

Essa classe possui os métodos de ordenação e os atributos numbers, helper e number os quais representam respectivamente, o array de valores, o array auxiliar e a variável usada para armazenar o tamanho do array de valores.

Esta implementação já possui uma otimização implementada para o problema de memória citado acima. Da forma que foi projetado, o array auxiliar só é carregado uma vez em memória visando aliviar o consumo.

Método sort

Esse método recebe o array original e realiza as seguintes operações:

  • Linha 8: passa os valores do array, recebido como parâmetro, para o array atributo, numbers.
  • Linha 9: Define o valor do atributo number onde, esse valor é o tamanho do array original.
  • Linha 10: Define a dimensão do array auxiliar onde, essa dimensão é a mesma do array original.
  • Linha 11: Realiza a chamada ao método mergeSort, passando como parâmetros o número 0 e o resultado da operação number - 1 onde, zero representa o início de qualquer array e number - 1 representa a última posição do array.


Método mergeSort

Esse método realiza a divisão já citada acima. Recebe dois parâmetros: a posição inicial e final do array.

  • Linha 16: Verifico se o parâmetro begin é menor que o parâmetro end. Essa verificação é necessária pois, ela realiza o controle da divisão do array. Caso a condição não seja verdadeira é porque o array está vazio ou, já está ordenado.
  • Linha 18: Calculo o meio do array visando a divisão do mesmo.
  • Linha 20: Realizo chamada recursiva passando como parâmetros begin e middle, ou seja, estou analisando a primeira metade do array dividido. Como esta é uma chamada recursiva, as linhas anteriores (16 e 18) serão executadas novamente, até o momento em que reste somente uma posição no array dividido n-vezes.
  • Linha 22: Realizo chamada recursiva passando como parâmetros middle +1 end, ou seja, estou analisando a segunda metade do array dividido. O processo de divisão segue de forma igual ao citado acima.
  • Linha 24: Realizo chamada ao método merge passando os parâmetros: begin, end e middle. Dependendo do nível de recursividade da execução do algoritmo, os valores dessas variáveis não estarão iguais as que estavam originalmente.


Método merge

Esse método realiza a ordenação. Recebe três parâmetros: a posição inicial, final e média do array.

  • Linha 31 até 33: Copia os valores do array original para o array auxiliar. Os valores copiados são limitados pelo intervalo definido pelas variáveis begin e end.
  • Linha 35 até 37: Declaro as variáveis i, j e k.
  • Linha 40 até 49: É aqui aonde a mágica acontece. A condição definido no loop while da linha 40 define que a iteração será feita do início da fatia esquerda até seu fim e depois do início da parte direita até seu fim. Essa condição é definida dessa forma porque sempre comparamos o grupo da esquerda com o da direita. Na imagem ou no gif animado é possível notar isso. Nas linhas 41 e 44, é avaliado quem é menor valor. Caso o valor da esquerda seja o menor, ele é copiado para o array original na posição certa (k) e a variável i é incrementada, caso contrário, o valor da direita é copiado e a variável j é incrementada. A variável k é incrementada fora do bloco condicional if pois, ele representa a posição final do valor ordenado logo, ele não precisa estar sendo incrementado onde é avaliado o menor valor e sim, depois que isso foi feito.
  • Linha 51 até 55: Nesse bloco são copiados os valores restantes da ordenação. O bloco acima não deixa o array original totalmente ordenado, ele só passa os menores valores para frente (ordenação crescente). Então, precisamos de uma outra iteração para recuperar os outros valores do array auxiliar e posicioná-los nas posições corretas. Como sabemos quais as posições corretas ? A variável k não é zerada logo, ele ainda guarda referência do último valor ordenado posicionado. As variáveis i e k devem ser incrementadas para realização do posicionamento.

Cumprimos mais uma etapa do nosso estudo. Entendemos a implementação do nosso programa. Vamos realizar alguns testes ?

Nossos testes serão feitos da seguinte forma: iremos montar um array um valores aleatórios e com as dimensões da primeira coluna. Depois de montado, iremos realizar a operação de ordenação 1 milhão de vezes e medir o tempo total. O tempo individual de cada processamento é calculado pela divisão do tempo total por 1 milhão.

Figura 4 - Testes feitos para medir a execução do algoritmo
Gráfico gerado a partir da planilha.

Figura 5 - Gráfico de quantidade de elementos por tempo
Através das evidências acima podemos notar que, conforme multiplicamos por 10 a quantidade de números, o resultado final também cresce da mesma forma. Esse algoritmo se mostrou eficaz para uma grande quantidade de números visto que, seu primo, Bubble Sort, demorou muito mais tempo.

Para fazer o download do algoritmo, clique aqui.

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.

Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
Merge sort - https://pt.wikipedia.org/wiki/Merge_sort
John von Neumann - https://pt.wikipedia.org/wiki/John_von_Neumann
Gif Merge Sort - https://upload.wikimedia.org/wikipedia/commons/c/cc/Merge-sort-example-300px.gif
Program: Implement merge sort in java. - http://java2novice.com/java-sorting-algorithms/merge-sort/
Mergesort in Java - Tutorial - http://www.vogella.com/tutorials/JavaAlgorithmsMergesort/article.html
Estruturas de dados e algoritmos em Java, Robert Lafore, 2004

Leia Mais ››

terça-feira, 10 de novembro de 2015

Non-break space, já ouviu falar ? - Regex em Java

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar. O que vamos ver hoje aqui é como identificar non-breaking spaces através de uma regex. Se você não sabe o que é uma regex ou, não está familiarizado com elas, recomendo que você dê uma olhada nos posts aqui do blog.

Mas, o que é um non-breaking space ?

Segundo a Wikipedia:
In word processing and digital typesetting, a non-breaking space (" ") (also called no-break space, non-breakable space (NBSP), hard space, or fixed space) is a space character that prevents an automatic line break at its position. In some formats, including HTML, it also prevents consecutive whitespace characters from collapsing into a single space.
In HTML, the common non-breaking space, which is the same width as the ordinary space character, is encoded as &nbsp; or &#160. In Unicode, it is encoded as U+00A0.
Resumindo: Um non-breaking space é um caractere em branco que evita uma quebra automática de linha em sua posição. Em HTML, o non-breaking space tem a mesma largura que o caractere espaço e codificado como &nbsp; ou &#160. Em Unicode, é codificado como U+00A0.

OBSERVAÇÃO: Não sei se você notou mas o mnemônico &nbsp tem as mesmas iniciais que non-breaking space. Coincidência ? Acho que não.

Já podemos construir nossa regex em Java ? A resposta é: sim.

 [\\s\\xA0]+  

Vamos entender melhor ?
  • \\s -> Representa um whitespace (caractere em branco).
  • \\xA0 -> Representa o non-breaking space. O \\x é usado para representar um caractere com valor hexadecimal. No nosso caso, o valor é 0xA0.
  • [ ] -> Representam a operação booleana or. No nosso caso queremos identificar ou espaços em branco ou non-breaking spaces.
  • + -> Representa um quantificador. Denota que algo pode se repetir 1 ou infinitas vezes.
Possível pergunta: "Porque na regex você quer capturar também whitespaces ? Não deveriam ser somente os non-breaking spaces?"

A resposta para essa pergunta é bem simples. Eu faço isso para fins de complementação da regex, ou seja, capturando os dois tipos de espaços, eu possuo uma regex mais completa mas, nada me impede que só tenha uma regex que capture espaços em branco ou só non-breaking spaces.

No caso de dúvida sobre os elementos que compõem a nossa regex, você pode dar uma olhada na documentação do Java, clicando aqui ou, dando uma olhada nas referências abaixo.

Diferentemente dos outros posts aqui do blog onde, na maioria das vezes existe um projeto prático, dessa vez eu deixo meu conselho para você, desenvolvedor, que está passando por esse problema. Caso seja importante que seus dados não cheguem sujos na base de dados, realize dois tratamentos. O primeiro é um tratamento via trim(), pode ser feito tanto em Javascript + JQuery ou, como em Java. Não existem preferências.

O segundo tratamento é a substituição dos non-breaking spaces por Strings vazias. Esse, eu recomendo fazer em Java, através do método replaceAll. Este método recebe dois argumentos. O primeiro é uma regex (lá você põe a regex que aprendemos agora) e o segundo é o caractere que será usado para substituição.

Irei deixar nas referências o link para a documentação do método replaceAll.

É isso, terminamos !!!!

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.

Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
Unidentified whitespace character in Java - http://stackoverflow.com/questions/1702601/unidentified-whitespace-character-in-java
Non-breaking space - https://en.wikipedia.org/wiki/Non-breaking_space
Class Pattern - http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
Classe String , método replaceAll - http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replaceAll(java.lang.String,%20java.lang.String)
Leia Mais ››

sábado, 31 de outubro de 2015

Canivete suíço JQuery

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar. O que vamos ver hoje aqui é uma coletânea de métodos e estruturas JQuery que, as vezes podem cair como uma luva no nosso dia a dia. Caso você não saiba o que é JQuery, pode ficar tranquilo que eu te explico.

Segundo o site jquery.com, JQuery é:
jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. With a combination of versatility and extensibility, jQuery has changed the way that millions of people write JavaScript.
Traduzindo em miúdos:

JQuery é biblioteca rápida, pequena e repleta de características, construída em JavaScript. Ela realiza manipulações de objetos do DOM, manipulação de eventos, animações, e torna a experiência Ajax muito mais simples e isso tudo com uma API fácil de se usar a qual, funciona através de diversos browsers. ...
Figura 1 - JQuery, escreva menos, faça mais

Essa API foi escrita por John Resig.
Figura 2 - Criador da API JQuery
Quem quiser dar uma olhada no site dele e ver os projetos em que ele está envolvido, clique aqui ou dê uma olhada nas referências.

Como descrito no parágrafo acima, o JQuery nos provê diversos métodos e estruturas para realizar diversos tipos de trabalho. A minha meta aqui é apresentar os que mais usei e os que acho mais "mão na roda" logo assim, formando um canivete suíço. Apresentar de forma minuciosa todos os métodos seria muito complicado pois, um post muito extenso, seria o resultado.

Nosso canivete constará de seletores e métodos.

Seletores
  • $(elemento)
  • $("#id")
  • $(".class")
Falar de seletores é algo básico para quem quer aprender JQuery e minha primeira experiências com essas lindas estruturas as quais, tornariam-se minhas amigas no futuro foi um tanto complicado. De vez em sempre, dúvidas apareciam sobre JavaScript e eu tinha de pesquisar na internet. Nas minhas pesquisas, sempre encontrava pessoas usando esses seletores acima e eu me perguntava:

"Má que raios é isso ?"

Então, um dia eu achei a solução. Os seletores JQuery são estruturas providas pela API que nos permitem realizar buscas por elementos na nossa árvore DOM. Podemos pesquisar por ids, classes css, por tipo de elemento (tag html), por tipo de elemento especificando atributos, fazer casting de objetos implícitos JavaScript, etc.

$(elemento)

É possível acessar um objeto implícito JavaScript, como por exemplo o objeto document e fazer um casting para um objeto JQuery. A consequência disso é o acesso à métodos JQuery. Agora, quando você ver o trecho de código abaixo, não terá mais dúvidas.

Figura 3 - Seletor de elemento e o método ready
Tudo ficou claro não ? O que estamos fazendo acima, é o casting do objeto document e acessando o método ready do novo objeto JQuery criado. Aproveitando o "barco", podemos aprender um pouco sobre o método. Ele especifica uma função a ser executada quando o DOM é totalmente carregado. Nesta função handler podemos carregar imagens, realizar requisições AJAX, adicionar listeners a eventos, entre outras coisas. Nas referências, é possível acessar a documentação relacionada a este método.

$("#id")

Utilizamos o seletor $("#id") para obter elementos do nosso DOM através de seus respectivos ids. Para utilizar este seletor, é necessário informar qual é o id juntamente com caractere jogo da velha (#). Este seletor é muito parecido com algo que o próprio JavaScript nos fornece.

 document.getElementById("meuId")  

Exemplo:

 <html>  
 <head>  
      <title>Teste $('#id')</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
 </head>  
 <body>  
 <h1>Exemplo com seletor de Id</h1>  
 <input type="text" id="meuId" />  
 </body>  
 <script type="text/javascript">  
      $(document).ready(function(){  
           alert($('#meuId'));  
      });  
 </script>  
 </html>  

Caso existam mais um de 1 elemento com o mesmo id, o que será retornado é o primeiro a ser encontrado. Caso nada seja encontrado, é retornado um objeto vazio [ ].

$(".class")

Se o seletor de ids retorna um único elemento, o seletor de classes css funciona de forma contrária visto que, uma classe css pode ser aplicada a vários elementos HTML e um id não. Para utilizar este seletor, é necessário informar qual é a classe juntamente com caractere ponto (.).

Exemplo:

 <html>  
 <head>  
      <title>Teste Seletor de Classes</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
 </head>  
 <body>  
 <h1>Exemplo com seletor de classes css</h1>  
 <ul>  
      <li class="listagem">Primeiro</li>  
      <li class="listagem">Segundo</li>  
      <li class="listagem">Terceiro</li>  
      <li class="listagem">Quarto</li>  
      <li class="listagem">Quinto</li>  
 </ul>  
 </body>  
 <script type="text/javascript">  
      $(document).ready(function(){  
           alert($('.listagem'));  
      });  
 </script>  
 </html>  

Independemente da existência de 1 único elemento ou de vários, este seletor sempre retorna uma lista onde, o tamanho dela varia de acordo com a quantidade de elementos que possuem a classe css correspondente ao seletor. Caso nada seja encontrado, é retornado um objeto vazio [ ].

--------------------------------------------------------------------------------------------------------------------------

CURIOSIDADE: O caractere dólar $ utilizado nos seletores é um objeto JQuery. Este objeto é definido dentro da API e é possível modificar o caractere usado. Existem alguns método que são acessíveis diretamente do caractere dólar, para mais informações ver a documentação.

--------------------------------------------------------------------------------------------------------------------------

Recuperar elementos via JQuery é uma tarefa que já sabemos realizar. Então, vamos abordar métodos que agilizam o dia a dia do desenvolvimento de nossos softwares.

Métodos:
  • attr
  • css
  • each
  • hide
  • html
  • remove
  • show
  • val
attr

Retorna o valor de um atributo para o primeiro elemento do conjunto de elementos correspondentes ao seletor ou configura um ou mais atributos para todo elemento correspondente.

Exemplo:

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método attr</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
      <meta charset="UTF-8">  
 </head>  
 <body>  
 <h1>Exemplo método attr</h1>  
 <input type="text" id="meuId" />  
 <button onclick="mudarAttr()">Mudar</button>  
 </body>  
 <script type="text/javascript">  
      function mudarAttr(){  
           $('#meuId').attr('type','radio');  
           alert("Novo type do input: " + $('#meuId').attr('type'));  
      }  
 </script>  
 </html>  

css

Retorna o valor de uma propriedade css para o primeiro elemento do conjunto de elementos correspondentes ao seletor ou configura uma ou mais propriedades CSS para todo elemento correspondente.

Exemplo:

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método css</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
      <meta charset="UTF-8">  
 </head>  
 <body>  
 <h1>Exemplo método css</h1>  
 <input type="text" id="meuId" style="color: #FF0000" />  
 <button onclick="mudarCss()">Mudar</button>  
 </body>  
 <script type="text/javascript">  
      function mudarCss(){  
           $('#meuId').css('color','#00FF00');  
           alert("A nova cor do input é: " + $('#meuId').css('color'));  
      }  
 </script>  
 </html>  

each

Itera sobre objeto JQuery, executando uma função para cada elemento correspondente.

Exemplo:

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método each</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
      <meta charset="UTF-8">  
 </head>  
 <body>  
 <h1>Exemplo método each</h1>  
 <ul>  
      <li>Primeiro</li>  
      <li>Segundo</li>  
      <li>Terceiro</li>  
      <li>Quarto</li>  
      <li>Quinto</li>  
 </ul>  
 <button onclick="each()">Mostrar</button>  
 </body>  
 <script type="text/javascript">  
      function each(){  
           $('ul li').each(function(index, value){  
                alert("Index: " + index + " - " + "valor: " + value);  
           });            
      }  
 </script>  
 </html>  

hide

Retira visibilidade de elementos. Este método é uma melhor escolha do que a manipulação de propriedades CSS (display='block' ou display='none') porque, ele funciona em múltiplos browsers. Logo, você não perderá tempo.

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método hide</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
      <meta charset="UTF-8">  
 </head>  
 <body>  
 <h1>Exemplo método hide</h1>  
 <ul id="meuUl">  
      <li>Primeiro</li>  
      <li>Segundo</li>  
      <li>Terceiro</li>  
      <li>Quarto</li>  
      <li>Quinto</li>  
 </ul>  
 <button onclick="hide()">Esconder</button>  
 </body>  
 <script type="text/javascript">  
      function hide(){  
           $('#meuUl').hide();  
      }  
 </script>  
 </html>  

html

Retorna o conteúdo HTML do primeiro elemento do conjunto de elementos correspondentes ao seletor ou configura o conteúdo HTML para todo elemento correspondente.

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método html</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
      <meta charset="UTF-8">  
 </head>  
 <body>  
 <h1>Exemplo método html</h1>  
 <textarea id="meuTextarea">Lorem ipsum dolor sit amet.</textarea>  
 <button onclick="html()">Mostrar</button>  
 </body>  
 <script type="text/javascript">  
      function html(){  
           alert("Conteúdo do textarea: " + $('#meuTextarea').html());  
      }  
 </script>  
 </html>  

remove

Remove um conjunto de elementos do DOM os quais, são correspondentes ao seletor JQuery.

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método remove</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>  
      <meta charset="UTF-8">  
 </head>  
 <body>  
 <h1>Exemplo método remove</h1>  
 <input type="text" class="remocao" value="1" /> <br/>  
 <input type="text" value="2"/> <br/>  
 <input type="text" value="3"/> <br/>  
 <input type="text" class="remocao" value="4"/> <br/>  
 <input type="button" onclick="remover()" value="Remover" />  
 </body>  
 <script type="text/javascript">  
      function remover(){  
           var length = $('.remocao').length;  
           alert("Quantidade de elementos removidos: " + length);  
           $('.remocao').remove();  
      }  
 </script>  
 </html>  

Note que quando os elementos sumiram, eles foram realmente removidos do DOM, ou seja, foram destruídos. Você não conseguirá acessar mais eles. A quantidade de elementos removidos é obtida através do length do array do seletor, conceito o qual, foi citado acima.

show

Dá visibilidade a elementos. Este método é uma melhor escolha do que a manipulação de propriedades CSS (display='block' ou display='none) porque, ele funciona em múltiplos browsers. Logo, você não perderá tempo.

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método show</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>  
      <meta charset="UTF-8" />  
 </head>  
 <body>  
 <h1>Exemplo método show</h1>   
  <ul id="meuUl" hidden>  
    <li>Primeiro</li>  
    <li>Segundo</li>  
    <li>Terceiro</li>  
    <li>Quarto</li>  
    <li>Quinto</li>  
  </ul>   
 <button onclick="show()">Mostrar</button>  
 </body>  
 <script type="text/javascript">  
      function show () {            
           $('#meuUl').show();  
      }  
 </script>  
 </html>  

val

Retorna o valor atual do primeiro elemento do conjunto de elementos correspondentes ao seletor ou define o valor de todo elemento correspondente.

 <!DOCTYPE html>  
 <html>  
 <head>  
      <title>Teste método val</title>  
      <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>  
      <meta charset="UTF-8" />  
 </head>  
 <body>  
 <h1>Exemplo método val</h1>   
 <input id="meuId" type="text" />  
 <br/>  
 <button onclick="val()">Mostrar valor</button>  
 </body>  
 <script type="text/javascript">  
      function val () {  
           alert("Antigo valor: " + $('#meuId').val());  
           $('#meuId').val('Novo valor !!');  
      }  
 </script>  
 </html>  

Para baixar todos os exemplos, clique aqui.

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.
Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
jQuery API - https://api.jquery.com/
John Resig - http://ejohn.org/
.ready() - http://api.jquery.com/ready/
.attr() - http://api.jquery.com/attr/
.css() - http://api.jquery.com/css/
.each() - http://api.jquery.com/each/
.hide() - https://api.jquery.com/hide/
.html() - https://api.jquery.com/html/
.show() - https://api.jquery.com/show/
.remove() - https://api.jquery.com/remove/
.val() - https://api.jquery.com/val/
Leia Mais ››

domingo, 27 de setembro de 2015

Vamos por ordem nessa bagunça ? - Algoritmos de ordenação

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

Hoje faremos algo diferente e inusitado aqui. Esse post será um catálogo para outros posts. Você pode estar se perguntando: "catálogo de que ?"

Bem, acredito que o nome do post é bem sugestivo, nosso catálogo será sobre algoritmos de ordenação e lá vem a sua segunda pergunta: " Porque criar um catálogo e não por o conteúdo logo aqui ?! "

A resposta é simples, algoritmo de ordenação é um assunto grande então, se eu fosse por todos os algoritmos aqui, não poderia dar a atenção devida pra cada um, o post ficaria extenso e isso acabaria cansando sua leitura. Tento escrever da melhor forma possível para que o conteúdo fique legal você, caro leitor.


Para cada novo post sobre ordenação lançado, vou atualizar esse post aqui, botando o link para o post recém criado. Espero que nosso catálogo fique imenso e que você se torne um expert nos algoritmos de ordenação.

Catálogo de algoritmos de ordenação
  1. Algoritmo BubbleSort - http://precisoestudarsempre.blogspot.com.br/2015/09/bolhas-no-tanque-o-algoritmo-bubble-sort.html
  2. Algoritmo Merge Sort - http://precisoestudarsempre.blogspot.com.br/2015/11/dividindo-e-conquistando-o-algoritmo.html
  3. Algoritmo Insertion - http://precisoestudarsempre.blogspot.com.br/2016/03/inserindo-as-coisas-certas-nos-lugares.html
  4. Algoritmo Shell Sort - http://precisoestudarsempre.blogspot.com.br/2016/08/a-criacao-de-donald-shell-o-algoritmo.html
  5. Algoritmo Selection Sort - https://precisoestudarsempre.blogspot.com.br/2017/10/aprendendo-escolher-bem-o-que-por-em.html

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.

Facebook: https://www.facebook.com/precisoestudarsempre/
Leia Mais ››

Bolhas no tanque - O algoritmo Bubble Sort

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

Este é o primeiro post da nossa série sobre algoritmos de ordenação e vamos começar com aquele algoritmo mais primordial que nos acompanhou em todo o período da faculdade, o Bubble Sort. Se você ainda não passou por isso, relaxe pois, passará. Se você já passou e não entendeu como ele funciona, agora é o momento da decisão.

O estudo que faremos aqui envolverá a construção do algoritmo em Java e alguns experimentos. Não usaremos ferramentas de desenvolvimento ultra modernas, um simples editor de texto bastará.

Qual é a lógica por trás desse algoritmo ? É simples, sua lógica consiste na troca da posições dentro do array de elemento por elemento, analisando quem tem o maior valor. O elemento de maior valor vai sendo movimentado para o fundo do array, até encontrar alguém maior que ele. Caso encotnre, o antigo maior valor pára de ser movimentado e o novo maior valor começa a ser movimentado para o fundo do array, respeitando a regra mencionada acima

Ficou confuso ? Vamos entender melhor.
Figura 1 - Funcionamento do algoritmo Bubble Sort
No gif animado acima é possível ver quer os elementos são avaliados par a par. Se o primeiro elemento do par for maior que o segundo ele é trocado de lugar, por exemplo os valores 6 e 5.
Figura 2 - Primeiro par escolhido

Após a troca de lugar, é avaliado o segundo par: 6 e 3.
Figura 3 - Segundo par escolhido
As trocas são realizadas somente se o primeiro elemento for maior que o segundo. Caso não seja, um novo par é escolhido. No gif animado é possível ver que em um determinado momento, o 6 forma par com o 8 mas, 6 não é maior que 8 logo, ele não é trocado e um novo par é feito. Todo este processo é repetido até que o array esteja ordenado.

Agora sim, temos tudo o que precisamos para construir nosso algoritmo.

      public void sort(int[] array, int tamanho){  
           int aux;  
   
           for(int i=tamanho-1; i >= 1; i--) {   
                for(int j=0; j < i ; j++) {  
                     if(array[j]>array[j+1]) {  
                          aux = array[j];  
                          array[j] = array[j+1];  
                          array[j+1] = aux;  
                     }  
                }  
           }  
      }  

O algoritmo é bem simples e tem complexidade O(n²) no pior caso. O segundo for executa uma varredura no sentido início - fim, avaliando se o primeiro elemento da dupla é maior que o segundo. O responsável pela troca de posições é o bloco de código dentro do if. O primeiro for trabalha de forma fim - início a fim de otimizar o algoritmo visto que, uma vez que o elemento de maior valor é jogado para o fim do array, não existe motivo para que uma dupla seja composta com esse valor. É possível ver isso acontecendo no gif animado, no momento em que o 8 (maior valor) é jogado para o fim do array.

Ótimo !!! Atingimos mais uma etapa. Primeiramente, entendemos a lógica do algoritmo e logo em seguida, vimos uma possível implementação. Para fechar com perfeição, vamos fazer algumas análises de tempo de processamento.

Dadas as situações testadas abaixo:

Figura 4 - Testes feitos para medir a execução do algoritmo
As quais geraram o seguinte gráfico:
Figura 5 - Gráfico de quantidade de palavras por tempo
É possível notar que conforme o número de elementos cresce de forma quadrática, o tempo do algoritmo cresce seguindo a família do 100, ou seja, 100 vezes, 200 vezes, etc.. Se continuássemos os testes para dez mil ou cem mil iríamos notar um aumento ainda maior no tempo de execução. Tal algoritmo, se aplicado a situações da vida real onde, a dimensão do problema pode ser maior ou menor que os testados aqui, pode gerar um certo atraso ou não, na execução do programa como um todo.

E com isso, terminamos o nosso estudo sobre o algoritmo Bubble Sort. Espero que você tenha gostado. Caso note alguma anomalia nos testes realizados, deixe sua contribuição nos comentários.

Para baixar o algoritmo, clique aqui.

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.

Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
Comparison Sorting Algorithms - https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.htmlA Comparative Study between Various Sorting Algorithms - http://paper.ijcsns.org/07_book/201503/20150302.pdf
Sorting Algorithm Animations - http://www.sorting-algorithms.com/
Bubble sort - https://pt.wikipedia.org/wiki/Bubble_sort
Leia Mais ››

domingo, 13 de setembro de 2015

OGNL + Struts 2 - Porque causa tanta confusão ?

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

O tema de hoje é voltado para desenvolvedores que já estão familiarizados com o framework Struts 2.  Não explicarei aqui como funcionam as tags do Struts pois, não é esse o nosso objetivo. Caso você, amiguinho, não conheça nada desse framework, recomendo fortemente que você dê uma clicada aqui e veja essa listagem de tutoriais.

Tenho experiência de alguns anos com o framework Struts 2 e toda vez que preciso recuperar dados usando OGNL, me sinto um pouco inseguro e desconfortável mas, porque ? Tenho de admitir que sou muito fã de Expression Language (EL) + JSTL porque, com eles consigo trabalhar de forma límpida e rápida. A verdade era que a OGNL não descia pela minha garganta porque, achava muito complicado, existem várias formas de se fazer a mesma coisa. Eu nunca sabia quando usar o #, o %{expressão}, o @ ou o attr e isso ainda podia piorar porque, as vezes via em alguns exemplos, pessoas realizando combinações desses caracteres. Isso tudo me estressava profundamente, eu não conseguia entender o que eu tinha de fazer e acabava testando combinações aleatórias, esperando a primeira funcionar. Porém, como o nosso foco não pode conhecer limites, tive que aprender OGNL e gostar dela.

A OGNL não é nada mais, nada menos que um tipo de linguagem de escopo de página, usada para recuperar dados dos diferentes contextos de uma aplicação. Antes que você me pergunte se é possível utilizar OGNL com outro framework que não seja o Struts 2, eu te respondo que nunca vi tal integração. Nem com o Struts 1, acho que é possível.

Depois de um certo tempo de pesquisa, consegui achar uma resposta para minha penosa dúvida. Tal resposta a qual, servirá para construir exemplos em uma aplicação web mostrando que a mesma é verídica. Então, aperte seus cintos.

Como disse anteriormente, a OGNL é usada para recuperar valores de um determinado escopo onde, esse escopo pode ser request, sessão ou aplicação. Porém, antes de sabermos de qual escopo queremos recuperar nosso dado, precisamos saber qual o seu nome logo, sempre que usarmos OGNL temos que especificar qual o nome do nosso objeto (objectName) tanto, para simples recuperações quanto, para montagem de expressões. Esta é a nossa premissa básica.

Vou separar as explicações por perguntas, acho que assim fica mais fácil para você encontrar.
  • Quando uso o # ? 
Usamos a tralha ou jogo da velha para fazer referência a objetos que estejam no nosso ActionContext, ou seja, no contexto da nossa action. Como assim ?
    • #objectName - Objeto que tenha sido criado usando as tags do struts com o escopo padrão.
    • #parameters.objectName - Um parâmetro do request.
    • #request.objectName  - Um atributo de escopo de request.
    • #session.objectName  - Um atributo de escopo de sessão.
    • #application.objectName  - Um atributo de escopo de aplicação.
    • #attr.objectName  - Um atributo que pode estar nos seguintes contextos: page, request, session ou application. A busca é feita nessa ordem.
Exemplo:
Figura 1 - Primeiro exemplo do uso da #
Figura 2 - Segundo exemplo do uso da #
Note que utilizarmos a OGNL sem as tags do Struts, as expressões são renderizadas como se fossem texto. É possível recuperar valores de um determinado escopo das seguintes formas:
    • #escopo.objectName
    • #escopo['objectName']
Se você entendeu o uso do # então, você já entendeu 75% do uso da OGNL no Struts 2. Parabéns, você já entendeu o mais complicado.
  • Quando uso o %{expressão} ?
Usamos o %{expressão} para forçar a OGNL avaliar o dado pelo seu real tipo ou para invocar um método, como por exemplo o método getText (usado para obter valores de arquivos de propriedades).

Figura 3 - Exemplo de uso do %{expressão}
Acima existem dois testes. Duas estruturas condicionais foram montadas, a primeira em cima de um atributo de um objeto e a segunda baseada em um atributo Integer da action. No primeiro if é necessário usar o %{} pois o Struts não conhece o tipo do objeto pessoa logo, ele não consegue fazer a comparação mas, como a segunda é feita em cima de um atributo do tipo Integer, não é necessário o uso do %{}. Condições realizadas em cima de tipos primitivos e seus wrappers não necessitam do %{}.

 <s:set name="var" value="%{myDinamicValue}" />  
   
 <s:set name="var" value="myDinamicValue" />  

No exemplo acima, o valor da nossa variável var, será o valor da variável myDinamicValue e no segundo exemplo, o valor final de var será a String myDinamicValue.
  • Quando uso o @ ?
Usamos a @ quando queremos fazer referência a recursos estáticos, ou seja, propriedades e métodos. Para utilizar essa facilidade, você deve habilitá-la em seu arquivo struts.xml, adicionando a propriedade: struts.ognl.allowStaticMethodAccess=true.

IMPORTANTE: A partir da versão 2.3.0, o acesso a recursos estáticos através de OGNL é deprecated.

Exemplo:
Figura 4 - Exemplo de uso do @
  • Quando usamos o $ ?
A OGNL não faz uso do $ logo, não irá funcionar. Caso você já tenha visto o uso do dólar acompanhado de chaves em tags do Struts 2, resultará em erro ou, em uma String comum.

Para baixar o projeto, clique aqui.

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.

Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
What's the difference between # , % and $ signs in Struts tags - http://stackoverflow.com/questions/8007858/whats-the-difference-between-and-signs-in-struts-tags
Leia Mais ››

quarta-feira, 26 de agosto de 2015

Virando um pedreiro de software - Padrão de projeto Builder

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

O tema de hoje será um pouco diferente dos temas que geralmente abordamos. Iremos focar mais na forma de se construir um software e menos na tecnologia empregada. Hoje falaremos de padrões de projeto.

Você já ouviu algo sobre padrões de projeto ou sabe o que são ? Se sim, pule esta parte do post caso contrário, é interessante que você gaste cinco minutinhos lendo esse trecho introdutório.

Já sabe como o padrão Builder funciona e quer dar uma olhada no projeto ? Clique aqui.

Os padrões de projeto nasceram com Christopher Alexander e ao contrário do que você pensa, Christopher não era da área de T.I. mas sim, da área de arquitetura civil. Nesse momento você pensa: Como um cara da área civil pode ter criado padrões que são utilizados em softwares ?

Vamos com calma !! 

Christopher notou que padrões podiam ser estabelecidos na área da construção civil então, ele publicou em 1977 um catálogo com mais de 250 padrões para a arquitetura civil, que discutiam questões comuns da arquitetura, descrevendo em detalhe o problema e as justificativas de sua solução.

O que isso trouxe de vantagem ?

Muita coisa visto que, se uma outra pessoa tivesse um problema o qual, já estivesse catalogado, ela poderia atingir facilmente atingir a solução ou, se o problema fosse parecido, ela poderia adaptar a solução.

Porém, é possível dizer que um problema e sua solução formam um padrão por si só ?

A resposta é não. Para podermos afirmar que um problema e sua solução formam um padrão, eles devem atender as seguintes características:
  • o problema
  • a solução
  • o contexto (ambiente)
  • regularidade
Para existir um padrão, um problema deve existir, obviamente. Se um problema existe, ele deve ter uma solução, o que é óbvio também. Tanto o problema quanto a solução estão em um determinado ambiente, ou seja, um contexto. A regularidade é a frequência que aquela solução se aplica ao problema em diversos contextos. Se tivermos um problema, com uma solução em um determinado contexto e ela (solução) não puder ser aplicada em outros contextos parecidos, não temos um padrão. Do que adianta a solução de um problema que só acontece em um determinado ambiente ?

A idéia de um padrão não é essa. Os padrões de projetos tornam mais fácil reutilizar soluções e arquiteturas bem sucedidas para construir estruturas de forma flexível e fácil de manter.

No âmbito do desenvolvimento de software não é diferente. Projetistas e arquitetos lidam diariamente com diversos problemas e através da observação dos mesmos, foram construídas formas de contornar ou solucionar esses problemas. Neste post, aprenderemos um desses diversos padrões criados. Como existem muitos problemas no desenvolvimento de software, os padrões foram divididos em categorias. A categoria que iremos abordar é a de criação. Nela são discutidas formas de criação de objetos.

E de repente uma wild pergunta appers: "Ué !?!?!? Formas de criação de objeto ? Mas para eu criar um objeto não basta só dar o new ? Porque eu preciso de um padrão só para isso !?"

Sua pergunta será respondida através dos diversos conceitos que iremos aprender a partir de agora.

A criação de objetos pode ser algo banal em sistemas pequenos ou caseiros mas, quando se está em um ambiente corporativo, com milhares de sistemas, repletos de regras de negócio pesadas, a lógica do negócio pode se misturar com a lógica de criação do objeto e o que era fácil torna-se preocupante ou, até mesmo difícil. O padrão Builder separa a construção de um objeto complexo ou produto, de sua representação de modo que o mesmo processo de construção possa criar diferentes representações.

No nosso exemplo, criaremos robôs logo, precisamos definir como será nosso objeto complexo. Exagerei na quantidade de atributos para tentar refletir ao máximo a realidade de um sistema pesado.

 public class Robo {  
      private Date dataDeFabricacao;  
      private String nome;  
      private String[] cores;  
      private String especialidadeDeCombate;  
      private String fonteDeAlimentacao;  
      private String tipoBlindagem;  
      private int quantidadeDeBracos;  
      private int quantidadeDeCabecas;  
      private int quantidadeDePernas;  
      private int quantidadeDeMisseis;  
      private int quantidadeDePilotos;  
      private int quantidadeDeCameras;  
      private int quantidadeDeCanhoes;  
      private boolean isAnalogico;  
      private boolean possuiCanhoPlasma;  
      private boolean possuiEspada;  
      private boolean possuiVoo;  
      private boolean possuiEsteira;  
      private boolean possuiEscudo;  
   
     /*gets e sets aqui*/  
 }  

Vamos definir os passos de construção dos nossos robôs, ou seja, o nosso Builder.

 public abstract class RoboBuilder {  
   
      protected Robo robo;  
        
      public RoboBuilder() {  
           this.robo = new Robo();  
      }  
        
      public Robo getRobo(){  
           return this.robo;  
      }  
        
      public abstract void buildCaracteristicasGerais();  
      public abstract void buildCabeca();  
      public abstract void buildTronco();  
      public abstract void buildBracos();  
      public abstract void buildPernas();  
      public abstract void buildSistemaDeArmas();  
      public abstract void buildSistemaDeVoo();  
      public abstract void buildSistemaDeDefesa();  
 }  

O nosso Builder é uma classe abstrata porque não definiremos aqui os comportamentos dos nossos robôs mas sim, quais passos devemos realizar para construir um robô. Se no Builder definimos só s passos então, onde iremos definir como cada robô funciona, ou seja, seu comportamento ? Isso será feito em classes concretas e lá iremos de fato, definir minunciosamente como cada robô opera.

Classe GipsyDangerBuilder


 public class GipsyDangerBuilder extends RoboBuilder {  
   
      @Override  
      public void buildCaracteristicasGerais() {  
           super.robo.setNome("GipsyDanger");  
           try {  
                super.robo.setDataDeFabricacao(new SimpleDateFormat("dd/MM/yyyy").parse("18/10/2025"));  
           } catch (ParseException e) {  
                e.printStackTrace();  
           }  
           super.robo.setCores(new String[]{"azul turquesa","branco"});  
      }  
        
      @Override  
      public void buildCabeca() {  
           super.robo.setQuantidadeDePilotos(2);  
           super.robo.setQuantidadeDeCabecas(1);  
           super.robo.setQuantidadeDeCameras(15);  
      }  
   
      @Override  
      public void buildTronco() {  
           super.robo.setAnalogico(true);  
           super.robo.setFonteDeAlimentacao("Nuclear");  
      }  
   
      @Override  
      public void buildBracos() {  
           super.robo.setQuantidadeDeBracos(2);  
      }  
   
      @Override  
      public void buildPernas() {  
           super.robo.setQuantidadeDePernas(2);  
           super.robo.setPossuiEsteira(false);  
      }  
   
      @Override  
      public void buildSistemaDeArmas() {  
           super.robo.setEspecialidadeDeCombate("Luta corpo a corpo");  
           super.robo.setQuantidadeDeMisseis(30);  
           super.robo.setQuantidadeDeCanhoes(25);  
           super.robo.setPossuiCanhoPlasma(true);  
           super.robo.setPossuiEspada(true);  
      }  
   
      @Override  
      public void buildSistemaDeVoo() {  
           super.robo.setPossuiVoo(false);  
      }  
   
      @Override  
      public void buildSistemaDeDefesa() {  
           super.robo.setTipoBlindagem("Aço");  
           super.robo.setPossuiEscudo(false);  
      }  
 }  

Classe ChernoAlphaBuilder


 public class ChernoAlphaBuilder extends RoboBuilder {  
        
      @Override  
      public void buildCaracteristicasGerais() {  
           super.robo.setNome("ChernoAlpha");  
           try {  
                super.robo.setDataDeFabricacao(new SimpleDateFormat("dd/MM/yyyy").parse("10/07/2020"));  
           } catch (ParseException e) {  
                e.printStackTrace();  
           }  
           super.robo.setCores(new String[]{"verde oliva"});  
      }  
        
      @Override  
      public void buildCabeca() {  
           super.robo.setQuantidadeDePilotos(2);  
           super.robo.setQuantidadeDeCabecas(1);  
           super.robo.setQuantidadeDeCameras(10);  
      }  
   
      @Override  
      public void buildTronco() {  
           super.robo.setAnalogico(true);  
           super.robo.setFonteDeAlimentacao("Energia solar");  
      }  
   
      @Override  
      public void buildBracos() {  
           super.robo.setQuantidadeDeBracos(2);  
      }  
   
      @Override  
      public void buildPernas() {  
           super.robo.setQuantidadeDePernas(2);  
           super.robo.setPossuiEsteira(false);  
      }  
   
      @Override  
      public void buildSistemaDeArmas() {  
           super.robo.setEspecialidadeDeCombate("Luta corpo a corpo e uso de artilharia pesada");  
           super.robo.setQuantidadeDeMisseis(50);  
           super.robo.setQuantidadeDeCanhoes(33);  
           super.robo.setPossuiCanhoPlasma(false);  
           super.robo.setPossuiEspada(false);  
      }  
   
      @Override  
      public void buildSistemaDeVoo() {  
           super.robo.setPossuiVoo(false);  
      }  
   
      @Override  
      public void buildSistemaDeDefesa() {  
           super.robo.setTipoBlindagem("Aço cromo com adição de carbono");  
           super.robo.setPossuiEscudo(false);  
      }  
 }  

Quem utilizam as representações do Builder para iniciar a construção dos nossos robôs é a classe Director.

 public class FabricaDirector {  
      protected RoboBuilder roboBuilder;  
        
      public FabricaDirector(RoboBuilder roboBuilder) {  
           this.roboBuilder = roboBuilder;  
      }  
        
      public void buildRobo(){  
           roboBuilder.buildCaracteristicasGerais();  
           roboBuilder.buildCabeca();  
           roboBuilder.buildTronco();  
           roboBuilder.buildBracos();  
           roboBuilder.buildSistemaDeArmas();  
           roboBuilder.buildSistemaDeDefesa();  
           roboBuilder.buildPernas();  
           roboBuilder.buildSistemaDeVoo();  
      }  
   
      public Robo getRobo() {  
           return roboBuilder.getRobo();  
      }  
 }  

Nossa classe Director tem um método que inicia a construção, um método que retorna o robô construído e um atributo do tipo RoboBuilder o qual, é inicializado pelo construtor.

Analisando melhor:
  • O parâmetro do construtor é necessário porque assim, conseguimos passar qualquer tipo de Builder para o nosso Director e consequentemente, trabalhar com vários tipos de robôs.
  • O que torna nossa classe Director completamente flexível e desacoplada, é o fato do nosso Builder ser abstrato e suas representações, concretas. Isto é, no Director lidamos diretamente com os métodos definidos no Builder abstrato logo, não me importa como foram definidos (implementados) porque, no fim desse processo de produção, eu sei que o que será gerado, é um robô.
A classe que utiliza nosso Director para receber os robôs construídos, é uma classe cliente qualquer. Vejamos abaixo:

 public class Resistencia {  
      public static void main(String[] args) {  
           FabricaDirector fabricaDirector = new FabricaDirector(new GipsyDangerBuilder());  
           fabricaDirector.buildRobo();  
           Robo gipsyDanger = fabricaDirector.getRobo();  
             
           System.out.println("Ao ataque !!!");  
           System.out.println("Nome: " + gipsyDanger.getNome() + " - Especialidade de combate: " + gipsyDanger.getEspecialidadeDeCombate());  
             
           fabricaDirector = new FabricaDirector(new ChernoAlphaBuilder());  
           fabricaDirector.buildRobo();  
           Robo chernoAlpha = fabricaDirector.getRobo();  
             
           System.out.println("Ao ataque !!!");  
           System.out.println("Nome: " + chernoAlpha.getNome() + " - Especialidade de combate: " + chernoAlpha.getEspecialidadeDeCombate());  
      }  
 }  

A classe Resistencia necessita produzir robôs para o "ataque" (se você viu o filme Pacific Rim, sabe do que eu to falando) e utiliza a classe Director para tal. Note que ela não tem o mínimo conhecimento de como os robôs são produzidos, a única coisa que ela precisa saber é o que pedir, ou seja, se ela precisa de um robô Cherno Alpha ou um Gipsy Danger (põe no google que você vai ver o que são). Ela solicita o desejado para a classe Director e a mesma inicia a construção do robô e devolve ao cliente o robô construído e pronto para funcionar.

Mais uma vez atingimos o desacoplamento e a flexibilidade. A imagem abaixo mostra como as classes do nosso exemplo se relacionam.
Figura 1 - Diagrama de classe
Para baixar clique aqui.

Agradecimentos a Leonardo Palmeiro pela sugestão de post.

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários ou na nossa página do facebook.

Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
  1. Mão na massa: Builder - https://brizeno.wordpress.com/2011/09/25/mao-na-massa-builder/
  2. Rocha, Helder. J930: GoF Design Patterns em Java
  3. Guerra, Eduardo. Design Patterns com Java - Projeto orientado a objetos guiado por padrões. Casa do código
  4. Leite, Alessandro FerreiraConheça os Padrões de Projeto. http://www.devmedia.com.br/conheca-os-padroes-de-projeto/957#ixzz3PerhRtcc
Leia Mais ››