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

terça-feira, 16 de junho de 2015

Vamos fazer um chat ?

Olá amigos do Preciso Estudar Sempre, meu nome é João Paulo e minha paixão é estudar. O tema que estudaremos hoje é de nível avançado e para que possamos desenvolvê-lo, precisaremos antes entender o que são Sockets e Threads.

Se você já possui conhecimento sobre esses dois assuntos, pode baixar o projeto clicando aqui e seja feliz. :D

Maaaaaaaaaaaas, se você não sabe e quer saber, continue lendo este post, entenda o que explicarei, aí sim, baixe o projeto. Não recomendo baixar antes porque ter algo em mãos que não conseguimos entender não é tão legal.

O que é um Socket? O que um Socket faz ? Você já ouviu falar sobre isso ? Se você possui essa ou mais perguntas, relaxe, pegue um café, acenda um cigarro e confia no pai que, o inimigo cai.

Um socket é nada mais nada menos que, a ponta de uma comunicação de duas vias entre programas em uma rede de computadores. Ele é vinculado a uma porta de rede para que, a camada TCP possa identificar a aplicação que, destina dados para alguma outra aplicação.

Fácil, não !?

Você lendo isso e pirando: "Ainnn João, ainda não entendi, tem como dar um exemplo?"

Bem, vamos lá ! Vamos imaginar a seguinte: Você está digitando seu texto em seu editor de texto que você mais ama no mundo e precisar imprimir ele. O que acontece quando você clica no ícone de imprimir ?

O seu editor de texto cria um socket para a impressora, onde ele passa o ip e a porta para o socket e se conecta. Depois de conectado, ele envia os dados para a impressão. A impressora, por sua parte, já espera conexões. Ela realiza essa espera ouvindo uma porta de rede. Qualquer requisição feita para essa porta, será ouvida por ela onde, ela aceitará ou não essa conexão.

Então, se você entendeu o que eu escrevi aí em cima, você já tá "por dentro" do que é um socket. Vamos entender as threads ?

As threads são mais fácies ainda. Uma thread é um fluxo de dados executado de forma paralela junto ao processo que originou-o, por exemplo: Você já notou naquela barrinha de carregar quando você abre um programa? Aquela barra representa o progresso da inicialização do programa. Nessa inicialização, uma thread é iniciada para processar dados necessários que o programa precisará após ter sido carregado. Desta forma, há uma melhora na performance do programa. Tente imaginar o quanto o programa ficaria mais lento se ele não pudesse executar mais de uma tarefa por vez ?

Poxa que legal !!! Já entendemos os dois conceitos básicos para construir um chat. Agora vamos entender como um chat funciona e depois disso listar o que precisamos.

Para que um chat possa funcionar precisamos de clientes, um servidor, sockets, threads e canais de comunicação.
Figura 1 - Arquitetura do chat
Os cliente são as pessoas se falando onde, cada um possuirá sua própria tela de chat a qual, construiremos adiante. Cada cliente não tem noção de quantas pessoas estão no chat e para qualquer mensagem enviada, todos são notificados. O servidor é um programa que armazena todos os cliente conectados à ele e recebe todas as mensagens e redireciona as mesmas para todos, até para quem enviou. Isto é feito para que, o remetente de uma mensagem possa ver o que enviou.

Para que um cliente possa se conectar a um servidor, ele abre um socket e executa todo o processo explicado acima. Após a conexão ter sido feita, tanto o cliente quanto o servidor podem possuir controle sobre os fluxos de dados visando ler ou escrever dados.

Para sair da teoria e ir para a prática, vamos precisar de:
  • JDK instalado
  • Netbeans (estou usando a versão 7.4)
IMPORTANTE: Nosso chat será desenvolvido utilizando a tecnologia Java Swing. Se você não está familiarizado com ela, recomendo fortemente que você estude primeiro e depois volte pra cá porque, não explicarei Swing aqui.

IMPORTANTE: O layout das telas foi desenvolvido no Netbeans. Caso você queira importar o projeto daqui para o Eclipse, não recomendo logo, poderei ajudar.

Mãos na massa !

1 - Crie uma pasta em algum local de preferência sua.

2 - Abra o Netbeans e crie os projetos Cliente e Servidor na pasta recém criada. 

3 - Crie a classe Servidor no projeto Servidor.
package servidor;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class Servidor {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        List<PrintStream> clientes = new ArrayList<>();
        
        try {
            ServerSocket server = new ServerSocket(3306);
            Socket socket;

            while (true) {
                socket = server.accept();
                /*registro dos clientes*/
                clientes.add(new PrintStream(socket.getOutputStream()));
                new EnviadorMensagem(socket, clientes).iniciarMensagens();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    
}

Neste momento vamos focar somente bloco try. Lembra que eu expliquei lá em cima que o servidor (impressora) fica esperando conexões em uma determinada porta ? Então, o nosso não será diferente. Nosso servidor esperará conexões na porta 3306. Se você já trabalhou com MySQL, sabe que esta porta é utilizada por ele. Utilizei ela para que não precisasse abrir uma nova porta no meu roteador e é esse o jump of the cat (pulo do gato). Se você quiser fazer isso também é fácil, é só parar o serviço do MySQL.

Construímos esse loop infinito para que ele aguarde por infinitas conexões. Geralmente, loops infinitos consomem muita CPU mas, esse não será o nosso caso pois, o Servidor fica parado na primeira linha do loop onde, ele espera a conexão. Logo após da conexão ter sido estabelecida, o loop continua.

4 - Crie a classe Cliente no projeto Cliente.
package cliente;
import javax.swing.JOptionPane;

/**
 *
 */
public class Cliente {
    public static void main(String[] args) {
        while (true) {            
            String nomeCliente = JOptionPane.showInputDialog(null, "Informe seu nome", "Configurações iniciais", JOptionPane.INFORMATION_MESSAGE);
            if(nomeCliente == null || nomeCliente != null && "".equalsIgnoreCase(nomeCliente.trim())){
                JOptionPane.showMessageDialog(null, "O campo de nome do cliente é obrigatório.", "Dados obrigatórios", JOptionPane.ERROR_MESSAGE);
            } else {
                Chat chat = new Chat(nomeCliente);
                chat.setVisible(true);
                break;
            }
        }
    }
}

Essa classe é de fácil entendimento e eu dispenso explicações.

5 - Crie a classe Chat, no projeto Cliente e desenvolva a seguinte tela.
Figura 2 - Protótipo da Tela
6 - Crie a conexão com o servidor no construtor da classe Chat.
    public Chat(String nomeCliente) {
        initComponents();
        this.nomeCliente = nomeCliente;
        try {
            socket = new Socket("127.0.0.1", 3306);
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Não foi possível conectar ao servidor.", "Erro", JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
        jLabel1.setText(MessageFormat.format(jLabel1.getText(), nomeCliente));
        iniciarThread();
        this.escolherCor();
        this.configurarFonte();
        jTextArea1.requestFocus();
    }

No bloco try fazemos o que foi explicado no início desse post. O cliente está solicitando uma requisição ao servidor o qual, está no ip 127.0.0.1 (localhost) e na porta 3306. Vamos entender agora o método iniciarThread().

private void iniciarThread(){
        this.thread = new Thread(new Runnable() {
            String mensagemServidor;
            @Override
            public void run() {
                try {
                    inputStreamReader = new InputStreamReader(socket.getInputStream());
                    bufferedReader = new BufferedReader(inputStreamReader);
                    entrarSala();
                    while((mensagemServidor=bufferedReader.readLine())!=null){
                        try {
                            int indiceColchetes2 = mensagemServidor.indexOf("],[");
                            String message = mensagemServidor.substring(mensagemServidor.indexOf('[')+1,indiceColchetes2);
                            mensagemServidor = mensagemServidor.substring(indiceColchetes2+2);
                            Integer color = Integer.valueOf(mensagemServidor.substring(1,mensagemServidor.indexOf(']')));
                            StyleConstants.setForeground(styleNomeCliente, new Color(color));
                            styledDocument.insertString(styledDocument.getLength(), message + "\n", styleNomeCliente);
                        } catch (BadLocationException ex) {
                            ex.printStackTrace();
                        }
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
        thread.start();      
    }

Como o próprio nome já diz, esse método abre uma thread mas, para que precisamos dela ? Lembra o que falei sobre a execução paralela de tarefas ? Então, precisamos nesse momento executar de forma paralela, junto com a inicialização da tela, a abertura da stream de leitura do cliente para o servidor visando, ouvir as mensagens que o servidor envia para o cliente. De aonde conseguimos o input stream dessa comunicação cliente/servidor ? Do socket, obviamente. Senão fizéssemos tudo isso de forma paralela, a tela nunca abriria pois, o stream seria aberto e o programa iria ficar aguardando mensagens do servidor para o cliente.

7 - Crie o método que manda as mensagens para o chat e associe-o ao botão Enviar.
    private void enviarMensagem(){
        String mensagem = "["+nomeCliente + " diz: ";
        try {
            PrintStream printStream = new PrintStream(socket.getOutputStream());
            mensagem += jTextArea1.getText() + "],[" + this.corDaFonte.getRGB() + "]";
            printStream.println(mensagem);
            printStream.flush();
            jTextArea1.setText("");
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(null, "Não foi possível enviar a mensagem.", "Erro", JOptionPane.ERROR_MESSAGE);
        }
    }

Neste método abrimos o stream de escrita do cliente para o servidor, adicionamos a mensagem nele e enviamos. Mais uma vez, aonde conseguimos o output stream para a escrita dos dados do cliente para servidor ? Do socket, mais uma vez.

8 - Encerre a conexão e saia do chat.
     private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
        try {
            PrintStream printStream = new PrintStream(socket.getOutputStream());
            printStream.println("[[" + new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date()) + "] > " + nomeCliente + " saiu.],[" + this.corDaFonte.getRGB() + "]");
            printStream.flush();
            Thread.sleep(15);
            thread.stop();
            socket.close();
            System.exit(0);
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

Para sair do chat é necessário encerrarmos o socket e dar exit no programa. Eu encerrei a thread por uma questão de luxo mas, não é necessário. O sleep de 15 milisegundos é realizado porque antes do programa ser encerrado, o cliente envia uma mensagem para todos informando que saiu da sala. Caso o sleep não fosse feito, uma exception de escrita em socket seria propagada porque a velocidade do flush da stream é menor que a velocidade da execução do programa, ou seja, o flush não é imediato, ele demora um pouco.

9 - Cria a classe EnviadorMensagem no projeto Servidor.
package servidor;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.List;

/**
 *
 */
public class EnviadorMensagem {
    private Socket socket;
    private List<PrintStream> clientes;

    public EnviadorMensagem(Socket socket, List<PrintStream> clientes) {
        this.socket = socket;
        this.clientes = clientes;
    }
 
    public void iniciarMensagens(){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String mensagem = null;
                try {
                    InputStreamReader inputStreamReader = new InputStreamReader(socket.getInputStream());
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                    while ((mensagem=bufferedReader.readLine()) != null) {              
                        enviarMensagem(mensagem);
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
        thread.start();
    }
 
    private void enviarMensagem(String mensagem){
        for(PrintStream cliente : clientes){
            cliente.println(mensagem);
            cliente.flush();
        }
    }
}

Nesta classe ouvimos as mensagens que os clientes nos mandam e escrevemos as mensagens para os clientes. Precisamos iniciar uma thread aqui pelo mesmo motivo que precisamos iniciar uma thread na classe Chat, execução paralela de tarefas.

É somente possível enviar as mensagens para todos os clientes porque na classe Servidor mantemos uma lista de quem está conectado no servidor. Então, para um novo cliente que entra no chat, é adicionado à lista o stream de escrita daquele cliente. Como a lista é passada por referência para o construtor da nossa classe, ela sempre estará atualizada.

Antes de terminar, fazer um checklist de tudo o que construímos ?
  1. Criação de projetos no Netbeans OK
  2. Criação da recepção de conexões no lado servidor. OK
  3. Criação da conexão do lado cliente. OK
  4. Criação da thread do lado cliente. OK
  5. Abertura dos streams de leitura e escrita do lado cliente. OK
  6. Encerramento do chat. OK
  7. Abertura dos streams de leitura e escrita do lado servidor. OK
Pronto, construímos os alicerces base para o nosso chat. Os pontos adicionais que você já deve ter notado como, formatação da fonte, entre outros, não abordarei aqui porque senão o post ficará mais extenso do que já está.

Para baixar o projeto, clique aqui. Caso você não tenha entendido algum ponto que eu não tenha deixado claro, deixe sua dúvida aí nos comentários.

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 Is a Socket? - https://docs.oracle.com/javase/tutorial/networking/sockets/definition.html
Criando um chat com java (Parte 1) - https://www.youtube.com/watch?v=9__5MRYPVxc
Criando um chat com java (Parte 2) - https://www.youtube.com/watch?v=lqSEpj517Qc
Criando um chat com java (Parte 3) - https://www.youtube.com/watch?v=SzUZAvFFLI0
Leia Mais ››

sexta-feira, 5 de junho de 2015

Transformando XML em objetos Java com XStream

Olá amigos do Preciso Estudar Sempre, meu nome é João Paulo e minha paixão é estudar. Hoje falaremos sobre a transformação de XML em objetos Java. Para realizar essa transformação usaremos um framework chamado XStream. Esse framework facilita muito essa transformação, necessitando apenas de um Java Bean onde, a classe representará uma tag de maior nível e seus atributos representarão tag aninhadas.

Não entendeu !? Ficou confuso !? Calma, vou explicar melhor e vou explicar na prática.

Seeeeeee você está ansiosinho para fazer download do projeto, clique aqui.

Nossa situação é: precisamos ler de um arquivo XML os dados de um funcionário de uma empresa.

Para podermos trabalhar, precisaremos criar um projeto Maven. Não irei restringir qual tipo de projeto maven que você deve usar desde que, seja possível adicionar uma dependência no pom.xml e criar uma classe java.

Crie uma workspace e um projeto maven, adicione esta dependência no seu pom.xml.

 <dependency>  
      <groupId>com.thoughtworks.xstream</groupId> 
      <artifactId>xstream</artifactId> 
      <version>1.4.8</version> 
 </dependency> 

Crie o arquivo funcionario.xml em um lugar de preferência sua.

 <funcionario>  
      <nome>João Paulo</nome> 
      <email>meuemail@gmail.com</email> 
      <dtNascimento>28/03/1990</dtNascimento> 
      <cargo>Programador Java Senior</cargo> 
      <matricula>PGR 0001</matricula> 
      <telefones> 
           <telefone>2128906678</telefone> 
           <telefone>21988674322</telefone> 
           <telefone>21956099876</telefone> 
      </telefones> 
 </funcionario> 

Agora, crie a classe Funcionario.

 package XStreamProject.XStreamProject;  

 import java.util.Date; 
 import java.util.List; 

 public class Funcionario { 
      private String nome; 
      private String email; 
      private Date dtNascimento; 
      private String cargo; 
      private String matricula; 
      private List<Long> telefones; 

      public String getNome() { 
           return nome; 
      } 

      public void setNome(String nome) { 
           this.nome = nome; 
      } 

      public String getEmail() { 
           return email; 
      } 

      public void setEmail(String email) { 
           this.email = email; 
      } 

      public Date getDtNascimento() { 
           return dtNascimento; 
      } 

      public void setDtNascimento(Date dtNascimento) { 
           this.dtNascimento = dtNascimento; 
      } 

      public String getCargo() { 
           return cargo; 
      } 

      public void setCargo(String cargo) { 
           this.cargo = cargo; 
      } 

      public String getMatricula() { 
           return matricula; 
      } 

      public void setMatricula(String matricula) { 
           this.matricula = matricula; 
      } 

      public List<Long> getTelefones() { 
           return telefones; 
      } 

      public void setTelefones(List<Long> telefones) { 
           this.telefones = telefones; 
      } 
 } 

Crie a classe XMLToObject.java.

 package XStreamProject.XStreamProject;  

 import java.io.File; 
 import java.util.List; 
 import com.thoughtworks.xstream.XStream; 
 import com.thoughtworks.xstream.converters.basic.DateConverter; 

 public class XMLToObject { 

      public static void main(String[] args) { 
           new XMLToObject().transformXMLToObject(); 
      } 

      private void transformXMLToObject(){ 
           File nossoArquivoXML = new File("C://arquivos//funcionario.xml"); 
           XStream xStream = new XStream(); 

           /*configuracao*/ 
           xStream.alias("funcionario", Funcionario.class); 
           xStream.registerConverter(new DateConverter("dd/MM/yyyy", null)); 
           xStream.alias("telefones", List.class); 
           xStream.alias("telefone", Long.class); 
           Funcionario funcionario = (Funcionario) xStream.fromXML(nossoArquivoXML); 
           this.printReport(funcionario); 
      } 

      private void printReport(Funcionario funcionario){ 
           System.out.println("--- Relatório do funcionário ---"); 
           System.out.println("Nome: " + funcionario.getNome()); 
           System.out.println("Email: " + funcionario.getEmail()); 
           System.out.println("Data de nascimento: " + funcionario.getDtNascimento()); 
           System.out.println("Cargo: " + funcionario.getCargo()); 
           System.out.println("Matrícula: " + funcionario.getMatricula()); 
           if(!funcionario.getTelefones().isEmpty()){ 
                System.out.println("Telefones: "); 
                for (Long phone : funcionario.getTelefones()) { 
                     System.out.println("\t" + phone); 
                }           
           } 
      } 
 } 

Agora que já codificamos tudo, vamos entender o que escrevemos.

Nós temos três métodos, o primeiro é banal, é o nosso velho amigo main o qual, já conhecemos muito bem. O último método serve somente para imprimir os dados que foram obtidos através do XML, não devemos focar nossa atenção nele. Porém, o segundo método é o mais importante dos três e por isso foi deixado por último.

Nesse método criamos uma referência para o arquivo XML e instanciamos o XStream. Logo após isso, definimos os alias e registramos um converter. Os alias servem para dizer ao XStream que tipo é o dado que estamos tentando ler. Se isso não for feito, ele tentará atribuir uma String ao atributo mapeado o que, ocasionará em um erro de conversão. Não sei se você notou mas, mas os aliases só foram realizados nos dados que não são String.

O converter foi registrado porque, no XML a data de nascimento está no formato dd/MM/yyyy e o XStream não entende esse formato. Ele tem como formato padrão o yyyy-mm-dd hh:mm:ss e uma lista de padrões alternativos que seguem o padrão default. Se não tivéssemos registrado o converter, o XStream iria propagar um erro de conversão de dados.

Agora a parte mais importante de todo nosso estudo, a transformação dos dados. O XStream deixa isso muito simplificado através do método fromXML o qual, recebe a referência do nosso arquivo XML e usa as configurações feitas, ambas acima. O casting é feito porque esse método retorna um Object.

Pronto !! Você já tem tudo o que precisa para transformar dados de um XML em um objeto.

Para baixar o projeto, clique aqui.

Sugestões ?! Críticas ?! Elogios ?!

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


Referências:
Serializing Java Objects with XStream - http://www.xml.com/lpt/a/1462

Leia Mais ››

quarta-feira, 27 de maio de 2015

Spring o que ? SPRING ROO !!!!!

Olá amigos do Preciso Estudar Sempre. Antes que vocês perguntem porque eu fiquei quase 1 mês sem publicar nada, eu já antecipo a resposta. Tempo, foi o que faltou. Assuntos não faltam embora sugestões sejam sempre bem vindas mas, sem tempo as coisas ficam difícies. Peço desculpa à todos pelo grande intervalo.

Bem, já que terminamos a parte mi mi mi, vamos falar de algo bom (e não é da texpix) ? Você já ouviu falar sobre o Spring Framework, né !? Aposto que sim mas, você já ouviu algo sobre os outros projetos da comunidade Spring ? Não ? Como não ?!  É amigo, eu imagino que você deve estar se sentindo assim:

"Ainnnn João, eu mal sei o Spring normal, quem dirá os outros projetos ??? O que eu faço agora ?? Ai meu deusssuuu"

Calma amigão, o mundo não vai acabar. A Spring community possuem vários projetos, não tem como você saber de todos mas, caso você queira conhecer eles, eu sugiro que você dê uma olhada no site. Deixarei linkado nas referências.

Um desses projetos é o Spring Roo. Com ele é possível criar sistemas com linhas de comando. Sim, você não leu errado, eu disse linhas de comando assim como, as que digitamos em um console de um sistema operacional.

Sua reação: "Meuuuuuu deuuuussuuuu, to passsadddaa, não acredittttôôô !!!"

Não irei explicar neste post o que significa inversão de controle e injeção de dependência. O intuito aqui não é esse. O que iremos abordar aqui hoje é como realizar a instalação do Spring Roo e como usar. É recomendado conhecimento pleno em programação e Java para que, seja possível absorver todo o conhecimento que estou tentando passar.

Agora que você já se recuperou do choque de que, é possível construir aplicações Java de uma forma veloz com linhas de comando, você se pergunta o motivo de usar isso na sua empresa e o que é na verdade esse framework.

Vamos retirar um trecho do manual (deixarei linkado nas referências):
Spring Roo is an easy-to-use productivity tool for rapidly building enterprise applications in the Java programming language. It allows you to build high-quality, high-performance, lock-in-free enterprise applications in just minutes. Best of all, Roo works alongside your existing Java knowledge, skills and experience. You probably won't need to learn anything new to use Roo, as there's no new language or runtime platform needed. You simply program in your normal Java way and Roo just works, sitting in the background taking care of the things you don't want to worry about. It's an approach unlike anything you've ever seen before, we guarantee it!
Segundo a documentação, o Spring Roo é uma ferramenta de fácil uso para construção rápida de aplicações corporativas na linguagem de programação Java. Ele permite que você construa aplicações com alta qualidade, performance e flexibilidade em poucos minutos. O melhor de tudo é que o Roo trabalha junto com o conhecimento, habilidade e experiências Java que já existem. Você provavelmente não precisará aprender nada novo para usar o Roo, pelo fato de não ser necessário nenhuma nova linguagem ou plataforma de execução. Você simplesmente programa da forma que sempre programou e o Roo simplesmente funciona, ficando no background da aplicação, tomando conta das coisas que você não quer se preocupar. É uma abordagem diferente de tudo que você já viu antes, nós garantimos isso.

Acho que não é necessário mais explicações sobre o que é o Roo e porque usar. Para podermos começar a usar o Roo, precisaremos:
  • Java 6 ou 7, com varíavel $JAVA_HOME instalada.
  • Maven, deixarei nas referências linkado como faz a instalação.
  • Spring Roo 3.11, clique aqui para baixar.
  • Console do seu sistema operacional.
OBS.: Estou usando o Windows.

Vamos começar a instalação do Spring Roo.

1 - Ponha o arquivo .zip do Spring Roo em uma pasta de sua preferência, e descompacte o arquivo.

2 - Depois de descompactado, abra seu painel de controle.

3 - Abra as variáveis do sistema.
Figura 1 - Tela das variáveis de sistema
4- Crie a variável de sistema ROO_HOME, onde seu valor será o lugar aonde você descompactou o arquivo com extensão zip.
Figura 2 - Instalação da variável de sistema ROO_HOME
5 - Edite a variável de sistema Path e adicione no fim do valor da variável (não se esqueça dos pontos e vírgula) o seguinte valor: %ROO_HOME%\bin
Figura 3 - Edição da variável Path
6 - Confirme as alterações e reinicie sua máquina.

7 - Abra o prompt de comando e digite roo, espere um pouco e a tela abaixo deverá ser exibida.
Figura 4 - Spring Roo
Agora que já terminamos a instalação da ferramenta, podemos começar a produzir. Como disse acima, iremos construir um sistema em poucos minutos e ele será web com acesso à um banco de dados através de um provedor. Mas antes de começarmos a construção precisamos definir um escopo para ele. Então, vamos pensar um sistema de um lava-carros. Esse sistema será bem simples, até porque senão for simples não conseguiremos terminar este post.

O dono do lava-carros, o sr Prohazca, nos contratou para a construção de um sistema onde, ele quer cadastrar seus funcionários, os carros dos clientes e os serviços de lavagem.

Depois do levantamento de requisitos, construímos o diagrama de classes.
Figura 5 - Diagrama de classe
Não nos preocuparemos com a simplicidade da modelagem, nosso intuito aqui é construir o sistema.

Podemos por a mão na massa e construir o sistema ?

Sim !! 

Crie uma pasta e nomeie como workspace_roo. Dentro dessa pasta crie uma pasta chamada lavacarros e abra o prompt de comando. Após aberto, acesse a pasta recém criada e execute o comando roo.

Execute os comandos abaixo:

 project --topLevelPackage br.com.lavacarros  

Este comando cria um projeto Maven. Nele é possível determinar o caminho de pacote. Este pacote será o nosso pacote de nível mais alto. Com base nele, nosso groupId será determinado. Podemos referenciar esse nosso pacote através do caractere ~.

O Spring Roo oferece um comando chamado hint. Esse comando oferece uma dica onde, ele indica qual o próximo passo tomar, por exemplo: Se você acabou de criar a pasta do projeto, iniciou o roo ali dentro, não sabe o que fazer e executou o hint, o roo ensinará como é possível construir um projeto. Essa ajuda é muito importante para usuários não tão experientes.

Temos agora algo parecido com isso:
Figura 6 - Estrutura do nosso projeto
Agora vamos determinar a camada de persistência da nossa aplicação.

 jpa setup --provider HIBERNATE --database HYPERSONIC_IN_MEMORY  

É possível especificar diversos provedores de persistência e bancos de dados. Para mais detalhes consulte o manual o qual, estará linkado aqui embaixo com os dados do nosso projeto.

Vamos criar nossas entidades agora. Execute o seguinte comando.

 entity jpa --class ~.model.entity.Carro --testAutomatically  

Lembra do ~ que citamos acima ? Aí está ele. Estamos informando ao comando entity que queremos criar uma entidade JPA no pacote entity, pertencente ao pacote model do nosso pacote principal. Note também que marcamos esse comando com --testAutomatically. Isso significa que nosso projeto já estará integrado com testes automatizados do Selenium. Execute o comando acima para as entidades Servico e Funcionario.

Já estamos quase terminado, aguenta firme !!

Vamos criar os atributos das nossas entidades. Vamos começar pela classe Servico.

 field number --fieldName id --type java.lang.Long --class ~.model.entity.Servico  
 field date --fieldName dtServico --type java.util.Date --class ~.model.entity.Servico  

Adicione agora os atributos da classe Carro.

 field number --fieldName id --type java.lang.Long --class ~.model.entity.Carro
 field string --fieldName placa --class ~.model.entity.Carro --notNull
 field number --fieldName ano --type java.lang.Integer --class ~.model.entity.Carro
 field string --fieldName marca --class ~.model.entity.Carro --notNull
 field string --fieldName modelo --class ~.model.entity.Carro

Os comandos são auto explicativos. Adicione os atributos da classe Funcionario,

 field number --fieldName id --type java.lang.Long --class ~.model.entity.Funcionario  
 field string --fieldName nomeCompleto --class ~.model.entity.Funcionario --notNull --sizeMax 100
 field number --type java.lang.Long --fieldName cpf --class ~.model.entity.Funcionario --notNull
 field number --type java.lang.Long --fieldName rg --class ~.model.entity.Funcionario --notNull
 field date --type java.util.Date --fieldName dtNascimento --class ~.model.entity.Funcionario

Agora que já adicionamos os atributos em nossas classes, vamos criar seus relacionamentos.

 field reference --fieldName funcionario --type ~.model.entity.Funcionario --class ~.model.entity.Servico  
field reference --fieldName carro --type ~.model.entity.Carro --class ~.model.entity.Servico --notNull

Falta muito pouco para terminarmos. Execute os seguintes comandos:

 web mvc setup
 web mvc all --package ~.web

O comando acima escaneia todo o projeto a fim de, analisar todas as entidades e criar a camada web pertinente à ela. O atributo package é usado a fim de especificar o pacote padrão para a camada web de nossa aplicação.

Pronto !!! Agora sim !!! Terminamos !! Podemos por para funcionar. Digite exit no roo e execute o comando:

 mvn tomcat:run  

Além de termos um sistema construído em poucos instantes, conseguimos realizar seu deploy através de comando único. É importante ressaltar que os comandos do maven só funcionarão, se a pasta corrente do prompt for onde está o arquivo pom.xml.

O Spring roo oferece suporte a segurança, utilizando Spring Security, entre outras características. Para baixar nosso projeto pronto com a documentação do roo e o diagrama de classes, clique aqui.

Sugestões ?! Críticas ?! Elogios ?! 

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


Referências:
Site oficial do Spring: https://spring.io/
Link para baixar Spring Roo: http://projects.spring.io/spring-roo/#running-from-shell
Instalação do maven: https://maven.apache.org/download.cgi - seção Installation Instructions
Nosso projeto: https://www.dropbox.com/s/f4v7361gkwkml61/workspace_roo.rar?dl=0
Leia Mais ››