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 ››

quarta-feira, 5 de agosto de 2015

Usando reflection em classes não compiladas

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

O tema que abordaremos hoje é relacionado a uma dificuldade que, um dia eu tive e, agora, resolvi compartilhar com vocês, os quais podem estar passando pelo mesmo problema.

Acho interessante que você tenha um conhecimento intermediário em java, amiguinho. Para os apressadinhos de plantão, clique aqui para baixar o projeto.

Vamos lá ..... A situação é a seguinte: Você possui um arquivo java em alguma pasta e precisa usar reflection nessa classe para obter dados e realizar invocações.

Nesse momento você já pensou: "ha ha ... mole ! ". Caaaaaalmmaaaa, existem duas coisas que eu ainda não contei.
  1. Este arquivo não está no seu projeto logo, não está no seu classpath.
  2. Este arquivo não está compilado.
Acho que agora as coisas devem ter ficado um pouco mais complicadas, não !? 

A solução é: temos que compilar esse arquivo java via programação e através disso, gerar e acessar o arquivo .class. A partir do momento que gerarmos um objeto Class dessa nossa classe, a reflection rola fácil.

Vamos ao código.

1:  private void carregarClasseJava() {  
2:              
3:            File javaFile = new File("resources/MinhaClasse.java");  
4:            javaFile.getAbsolutePath();  
5:            File sourceFolder = new File("resources");  
6:            sourceFolder.getAbsolutePath();  
7:              
8:            /*Compila a classe*/  
9:            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();  
10:            compiler.run(null, null, null, javaFile.getPath());  
11:    
12:            /*Define o classloader da nossa classe recém compilada*/  
13:            URLClassLoader classLoader = null;  
14:            try {  
15:                 classLoader = URLClassLoader  
16:                           .newInstance(new URL[] { sourceFolder.toURI().toURL() });  
17:            } catch (MalformedURLException ex) {  
18:                 ex.printStackTrace();  
19:            }  
20:              
21:            /*Crio a representação da classe compilada para posteriormente usar a reflection.*/  
22:            Class clazz = null;  
23:            try {  
24:                 clazz = Class.forName("MinhaClasse", true, classLoader);  
25:            } catch (ClassNotFoundException ex) {  
26:                 ex.printStackTrace();  
27:            }  
28:              
29:            /*Crio a instância da minha classe. Não fiz o casting direto para MinhaClasse porque a mesma não está no pacote src.   
30:             * Caso você queira fazer isso, mova a classe para o pacote src e mude os caminhos dos objetos File.*/  
31:            Object instance = null;  
32:            try {  
33:                 instance = clazz.newInstance();  
34:            } catch (InstantiationException | IllegalAccessException e) {  
35:                 e.printStackTrace();  
36:            }  
37:       }  

Vamos as explicações !!

Da linha 3 até a linha 6, acessamos o arquivo e a pasta aonde ele está. É necessário acessar a pasta do arquivo porque ele se tornará nosso class loader.

Da linha 8 até 10, compilamos o arquivo. Note que depois da linha 10 ser executada, é possível visualizar o arquivo .class na pasta de origem.

Da linha 12 até 19, criamos o nosso class loader. Um class loader é uma classe que carrega outras classes, ou seja, ele carrega o bytecode da sua classe para a memória. Deixarei nas referências um ótimo artigo da DevMedia, explicando os class loaders. Vale a pena dar uma conferida.

Da linha 21 até 27 é onde a mágica acontece. É aqui que passamos para o class loader qual classe carregar. O nome passado deve ser o fully qualified name, ou nome absoluto, da classe. É composto pelo caminho completo de pacotes juntamente com o nome da classe. No nosso exemplo, o fully qualified name da nossa classe é o próprio nome da classe, pois ela não está dentro de um pacote. Porém, se ela estivesse dentro na estrutura de pacotes abaixo:

Figura 1 - Estrutura de pacotes
Seu nome absoluto seria: br.com.meuprojeto.MinhaClasse.

Da linha 29 até 36, já uso a reflection e crio uma instância da nossa classe recém carregada. Pronto !!!

Sei que este post está mais curto do que as postagens habituais mas, achei interessante trazer esse conhecimento para vocês, caros leitores.

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:
How do I programmatically compile and instantiate a Java class? - http://stackoverflow.com/questions/2946338/how-do-i-programmatically-compile-and-instantiate-a-java-class
Entendendo ClassLoaders em Java - http://www.devmedia.com.br/entendendo-classloaders-em-java/29076
Leia Mais ››

segunda-feira, 20 de julho de 2015

Conheça a verdade - Tabelas Hash(sem colisão)

Olá amigos do Preciso Estudar Sempre, meu nome é João Paulo e minha paixão é estudar. Hoje entraremos, mais uma vez, no grande reino das estruturas de dados. Para que possamos falar sobre esse assunto, é necessário que você, caro leitor, já possua conhecimentos sobre Java e OO.

Caso você precise muito baixar o projeto agora, clique aqui mas, se você aguenta mais um pouquinho, leia o post e baixe o projeto no link que tem lá embaixo, até porque dá uma trabalheira imensa trazer conteúdo de qualidade para vocês.

Voltando .... Imagine a seguinte situação: você possui um array com N elementos e quer procurar um elemento qualquer sendo que, você não sabe aonde ele está.

O que você faz ????

A primeira coisa que vem à nossa cabeça é uma busca sequencial, ou seja, verificar uma posição após a outra. Contudo, o custo de performance dessa busca é equivalente ao tamanho do array.

Nesse momento você pensa: "Ué, mas não existe a tal da busca binária, aquela que corta o array ao meio e elimina a outra metade ?"

Então, realmente existe a busca binária que faz isso tudo aí que você pensou mas, para a busca binária funcionar, ela tem como pré-requisito a ordenação do vetor, ou seja, você terá o custo da ordenação e da pesquisa. O ideal é podemos ter acesso direto ao elemento dentro do array.

É importante citar que nem sempre conhecemos a posição exata de um elemento dentro de um array logo, o acesso direto não é possível. Para tal, precisamos calcular essa posição, essa é a chave para a solução.

Como vamos calcular isso ?! E agora !?

Para que possamos contornar esse problema, precisamos sair da nossa zona de conforto, ou seja, dos arrays comuns que já conhecemos e precisamos aprender uma nova estrutura de dados, a tabela hash. Na tabela hash, não trabalhamos mais da forma tradicional, ou seja, de forma sequencial. Agora iremos espalhar nossos dados dentro de um array. Sim, você não leu errado, nós iremos espalhar nossos dados dentro de um array e, ainda te digo que isso permite que tenhamos o tão sonhado acesso direto.
Figura 1 - Comparativo entre tabela hash e lista

Nesse momento, imagino que você esteja realmente assustado.

Mas como iremos realizar esse espalhamento ? Vamos usar escolha randômica ? A resposta é não. Para conseguirmos espalhar com eficiência nossos dados, iremos utilizar uma função de espalhamento.

A função de espalhamento gera a posição em que vamos inserir nosso valor dentro do array. Essa posição é gerada através de uma chave passada como parâmetro. Cada chave deve ser única porque, estamos construindo uma tabela de hash sem colisões, visando um ambiente onde cada chave gere uma posição diferente. Caso existam chaves iguais, iremos gerar colisões, ou seja, duas inserções para uma mesma posição. Após nossa função de espalhamento ter sido executada, nosso acesso será reduzido para O(1).

Para uma função ser considerada uma função de espalhamento, ela deve preencher alguns pontos obrigatórios:
  • Ser simples e barata de se calcular.
  • Garantir que chaves diferentes produzam posições diferentes.
  • Garantir que não há preferência por posições

Existem diversos tipos de funções de espalhamento já conhecidas, vou listar algumas:
  • Método da divisão (iremos usar esta)
  • Método da multiplicação
  • Método da dobra
  • String como chave

Não explicarei como funciona cada função porque iremos estender muito nosso post e você ficará cansado. Caso as funções de espalhamento tenham gerado em você um grande interesse, fique tranquilo pois, farei um post exclusivo para isso.

Já entendemos tudo o que precisamos entender para ir para prática. Então, vamos ?

Crie a classe Aluno.

 public class Aluno {  
      private int matricula;  
      private String nome;  
      private float nota1;  
      private float nota2;  
      private float nota3;  
      public Aluno(int matricula, String nome, float nota1, float nota2,  
                float nota3) {  
           super();  
           this.matricula = matricula;  
           this.nome = nome;  
           this.nota1 = nota1;  
           this.nota2 = nota2;  
           this.nota3 = nota3;  
      }  
      //gets e sets  
 }  

A classe Aluno é uma classe simples, não preciso me estender em explicações sobre ela. Próximo passo.

Crie a classe HashTable.

 public class HashTable<T> {  
      private int qtd;  
      private int size;  
      private T[] objs;  
      private Set<Integer> keys;  
      public HashTable(int size) {  
           if(size == 0){  
                throw new IllegalArgumentException("O tamanho deve ser maior que 0");  
           }  
           this.size = size;  
           this.criarHashTable();  
      }  
      private void criarHashTable(){  
           this.objs = (T[]) new Object[this.size];  
           this.keys = new HashSet<Integer>();  
      }  
      public void add(T obj, int key){  
           if(this.qtd == this.size){  
                throw new IllegalStateException("Tabela cheia.");  
           }  
           int pos = this.chaveDivisao(key);  
           this.keys.add(key);  
           objs[pos] = obj;  
           qtd++;  
      }  
      private int chaveDivisao(int key) {  
           return (key & 0x7FFFFFFF) % size;  
      }  
      public T get(int key){  
           if(objs.length == 0){  
                return null;  
           }  
           int pos = this.chaveDivisao(key);  
           if(objs[pos] == null){  
                return null;  
           }  
           return objs[pos];  
      }  
      public T remove(int key){  
           if(objs.length == 0){  
                return null;  
           }  
           int pos = this.chaveDivisao(key);  
           if(objs[pos] == null){  
                return null;  
           } else {  
                T obj = objs[pos];  
                objs[pos] = null;  
                qtd--;  
                return obj;  
           }  
      }  
      public int size(){  
           return this.qtd;  
      }  
      public boolean isEmpty(){  
           return this.qtd == 0 ? true : false;  
      }  
      public Set keys(){  
           return this.keys;  
      }  
 }  

Vou ser pontual nas minhas explicações. Precisamos entender primeiramente o método criarHashTable(). Esse método é simples, ele inicia o array interno, o Set de keys interno e, é chamado dentro do construtor da HashTable. O Set de chaves foi criado visando a obtenção das mesmas para iteração de valores.

Note que o construtor da classe acima recebe um parâmetro o qual, representa o tamanho total da nossa HashTable. Além dos atributos objs keys os quais, já comentamos no parágrafo acima, também temos os atributos qtd size os quais, representam respectivamente, a quantidade de elementos presente no array e o tamanho total do array.

IMPORTANTE: Ao escolher o tamanho da HashTable, dê preferência a números primos pois, reduzem a probabilidade de colisões, mesmo a função de espalhamento não sendo muito eficaz. Caso você escolha um tamanho que seja potência de dois, a velocidade da HashTable será aprimorada mas a probabilidade de colisões pode aumentar caso a função de espalhamento seja muito simples.

A função add trabalha de forma bem simples. Ela calcula a posição baseada na chave a qual, é passada como parâmetro, insere o elemento naquela posição e incrementa o contador. A função remove trabalha de forma semelhante mas, antes de remover, verifica se existe algum valor naquela posição do array. Caso haja, remove o elemento, decrementa o contador e retorna o elemento removido.

A função get calcula a posição do elemento procurado através da chave passada e retorna o valor contido dentro do array naquela posição.

Uma das vantagens da HashTable, é que ela é muito fácil de se implementar.

Crie a classe Principal.

 public class Principal {  
      public static void main(String[] args) {  
           Aluno aluno1 = new Aluno(259379, "João Paulo Maida", 10, 7, 8);  
           Aluno aluno2 = new Aluno(1145, "Bruno Souza", 8, 9, 3);  
           Aluno aluno3 = new Aluno(55712, "Raphael Marques da Silva", 6, 7, 1);  
           Aluno aluno4 = new Aluno(19, "José da Silva", 1, 2, 3);  
           Aluno aluno5 = new Aluno(4754, "Luís Marcelo", 8, 5, 10);  
           Aluno aluno6 = new Aluno(4753, "Nathan Paulo Souza", 8, 5, 10);  
           Aluno aluno7 = new Aluno(1, "Beatriz Luana Campos", 7, 1, 2);  
           Aluno aluno8 = new Aluno(32, "Isadora Fernanda Monteiro", 6, 7, 7.5f);  
           Aluno aluno9 = new Aluno(0, "Marina Bruna da Silva", 2.5f, 3.5f, 8);  
           Aluno aluno10 = new Aluno(896358, "Levi Murilo Souza", 4.5f, 3, 9.7f);  
           
           HashTable<Aluno> hashTable = new HashTable<Aluno>(2048);  
           hashTable.add(aluno1, aluno1.getMatricula());  
           hashTable.add(aluno2, aluno2.getMatricula());  
           hashTable.add(aluno3, aluno3.getMatricula());  
           hashTable.add(aluno4, aluno4.getMatricula());  
           hashTable.add(aluno5, aluno5.getMatricula());  
           hashTable.add(aluno6, aluno6.getMatricula());  
           hashTable.add(aluno7, aluno7.getMatricula());  
           hashTable.add(aluno8, aluno8.getMatricula());  
           hashTable.add(aluno9, aluno9.getMatricula());  
           hashTable.add(aluno10, aluno10.getMatricula());  
           
           Aluno alunoRemovido = hashTable.remove(aluno1.getMatricula());  
           System.out.println(hashTable.size());  
           Aluno alunoRecuperado = hashTable.get(111);  
           System.out.println(alunoRecuperado);  
           Aluno alunoRecuperado2 = hashTable.get(55712);  
           
           System.out.println("Nome: " + alunoRecuperado2.getNome());  
           System.out.println("Matrícula: " + alunoRecuperado2.getMatricula());  
           System.out.println("Notas: " + alunoRecuperado2.getNota1() + "," + alunoRecuperado2.getNota2() + "," + alunoRecuperado2.getNota3());  
           System.out.println(hashTable.keys());  
      }  
 }  

A classe Principal só cria alunos e utiliza nossa API.

Pronto !! Chegamos ao fim de mais um post !!!!

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:
Aula 89 - Tabela Hash - Definição - https://www.youtube.com/watch?v=njkANXEMHTY
Aula 90 - Tabela Hash - Implementação - https://www.youtube.com/watch?v=K40yG9bmVZ4
Aula 91 - Tabela Hash - Criando e Destruindo a Tabela - https://www.youtube.com/watch?v=X55Ku_Mpw5g
Aula 92 - Função de Hashing - https://www.youtube.com/watch?v=o0TXB3QPOWY
Aula 93 - Tabela Hash - Inserção e busca sem tratamento de colisões -https://www.youtube.com/watch?v=sYKarxRQ_-g

Leia Mais ››

quarta-feira, 24 de junho de 2015

O que é um listener ? Como fazer um ?

Olá amigos do Preciso Estudar Sempre, meu nome é João Paulo e minha paixão é estudar. O tema de hoje é de nível intermediário e para falar sobre ele precisamos entender o básico de programação e de Java Swing. Caso você não possua esses conhecimentos, recomendo que você volte aqui um pouco mais tarde.

Vamos aprender aqui o que são listeners e como construir um nosso. Se você já sabe o que é um listener, pule essa parte da explicação mas, se você não sabe, aperte seu cinto pois, a viagem está preste a começar.

Um listener é um ouvinte. Nesse momento, você pergunta: "Como assim um ouvinte?" Bem, vamos melhorar essa definição !

Um listener é uma estrutura programada que ouve eventos os quais, foram registrados para ele. Ainda não entendeu ? Quando eu digo ouvir, não quer dizer que uma orelha magicamente irá aparecer na sua tela. Quero dizer que uma parte de seu programa será acionada assim que um evento for realizado. Por evento, podemos imaginar clicks, focus em campos, retirada de focus em campos, parar o mouse em cima de algo, tirar o mouse de algo, mover o mouse, mudar um valor, entre outros. Vamos para um exemplo ?

Imagine a seguinte tela:
Figura 1 - Tela
Quando clicamos no botão "Confirmar", os dados da tela devem ser gravadas no banco de dados mas, como o botão consegue acionar o método que realiza a gravação no banco ? A resposta é: precisamos adicionar um listener de click àquele botão. Após adicionado, o listener conseguirá ouvir todos os eventos de click daquele ou de vários botões e realizará o processamento programado.

No Java Swing isso é feito da seguinte forma:

1 - Classe da tela.
 //construtor da tela  
 public Tela(){  
      initComponents();  
      MeuClickListener meuClickListener = new MeuClickListener();  
      jButton1.addActionListener(meuClickListener);  
 }  

2 - Classe que trata os eventos de click
 public class MeuClickListener implements ActionListener{  
      @Override  
      public void actionPerformed(ActionEvent e){  
      }  
 }  

Independente de estarmos na web, desktop, mobile ou na programação distribuída, os conceitos continuam o mesmo. Acima temos, o evento, o listener e a associação, respectivamente, o click, a classe MeuClickListener e a associação feita através da linha jButton1.addActionListener(meuClickListener);  

Espero que o conceito base de listener tenha ficado bastante claro na sua mente pois, agora iremos construir o nosso.

Siga os seguintes passos:

1 - Crie um projeto Java em sua IDE.

2 - Crie a interface Listener.

 public interface Listener {  
      public void receivedEvent(Event event);  
 }  

A interface Listener representa um listener geral, o qual trata um evento qualquer. Se quisermos listeners mais específicos, como por exemplo: ActionListener, WindowListener, podemos criar nossas próprias interfaces, com nossos próprios métodos.

3 - Crie a classe ListenerClick.

 public class ListenerClick implements Listener {  
      @Override  
      public void receivedEvent(Event event) {  
           System.out.println("Eu trato os eventos de click desse sistema.");  
           System.out.println("Dados do evento recebido: ");  
           System.out.println("Nome: " + event.getName());  
           System.out.println("Hash code do objeto que gerou o meu evento: " + event.getHashCodeOrigem());  
      }  
 }  

Precisamos criar uma implementação para o nosso listener porque é aqui que trataremos de fato os eventos (MeuClickListener).

4 - Crie a classe Event.

 public class Event {  
   private String name;  
   private int hashCodeOrigem;  
   public Event(String name) {  
     super();  
     this.name = name;  
   }  
   public String getName() {  
     return name;  
   }  
   public void setName(String name) {  
     this.name = name;  
   }  
      public int getHashCodeOrigem() {  
           return hashCodeOrigem;  
      }  
      public void setHashCodeOrigem(int hashCodeOrigem) {  
           this.hashCodeOrigem = hashCodeOrigem;  
      }  
 }  

Essa classe representa o evento mas, podemos criar nossas próprias classes de eventos, com atributos e métodos próprios.

5 - Crie a classe Dispatcher.

 import java.util.HashSet;  
 import java.util.Iterator;  
 import java.util.Set;  
 public class Dispatcher {  
   private static Dispatcher instance;  
   private Set<Listener> listeners;  
   private Dispatcher() {  
     this.listeners = new HashSet<Listener>();  
   }  
   public static Dispatcher getInstance() {  
     if (instance == null) {  
       instance = new Dispatcher();  
     }  
     return instance;  
   }  
   public void addListener(Listener listener) {  
     this.listeners.add(listener);  
   }  
   public void removeListener(Listener listener) {  
     this.listeners.remove(listener);  
   }  
   public void dispatchEvent(Event event) {  
     Iterator<Listener> iterator = listeners.iterator();  
     while (iterator.hasNext()) {  
       Listener listener = (Listener) iterator.next();  
       listener.receivedEvent(event);  
     }  
   }  
 }  

A classe Dispatcher é uma das classes mais importante porque é nela onde os listeners serão adicionados ou removidos. Os listeners adicionados são mantidos em uma lista para que sejam disparados mais tarde os métodos de tratamento de eventos.

6 - Crie a classe Main.

 public class Main {  
      private ListenerClick listenerClick = new ListenerClick();  
      public Main() {  
           Dispatcher.getInstance().addListener(listenerClick);  
      }  
      public static void main(String[] args) {  
           Main main = new Main();  
           Event eventoOnClick = new Event("represento um onClick");  
           eventoOnClick.setHashCodeOrigem(main.hashCode());  
           Dispatcher.getInstance().dispatchEvent(eventoOnClick);  
      }  
 }  

A classe Main é uma classe de exemplo, é onde botamos nosso listener para funcionar. Nessa classe instanciamos um listener de clicks, adicionamos esse listener no dispatcher e logo em seguida, disparamos um evento para todos os listeners registrados.

Notou que os mesmos passos que realizamos no Swing são os mesmos no nosso próprio sistema ? Instanciamos um listener, criamos um evento e adicionamos o listener. A única diferença do nosso sistema para o Java Swing, o qual está sendo usado como exemplo, é que o dispatch é feita pela própria classe do JButton e no nosso sistema precisamos realizar essa dispatch manualmente.

Sinta-se à vontade para modificar esse projeto a vontade, o objetivo desse post é passar a idéia básica dos listeners. A partir daí, você pode evoluir o quanto quiser a idéia.

Para baixar o projeto pronto, clique aqui.

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

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


Referências:
Listener em Java - http://www.mews.com.br/dev/java/criando-listener-em-java/
Leia Mais ››