segunda-feira, 30 de setembro de 2013

Desenvolvimento dirigido por testes

Desenvolvimento dirigido por testes

O Desenvolvimento dirigido por testes ou Test driven development (TDD) é uma técnica de desenvolvimento de software criada
por Kent Beck que e baseia em um ciclo curto de repetições.

O TDD tem uma idéia bem simples e um roteiro bem definido:

- Escreve um teste
- Roda o teste e ve ele falhar
- Escreve o código que deve passar no teste
- Roda o teste novamente esperando que ele passe (Se falhar, significa que há um problema com o código funcional)
- Refatora o código
- Roda o teste eo teste passa
- Começa com um teste diferente


Exemplo: Calculadora

- Criar classe TesteCalculadora e cria o método TestaAdicao

public class TesteCalculadora {
    public void TestaAdicao(){
        assertEquals("Resultado da soma de 3 e 4 é 7",7,Calculadora.soma(3,4));
    }
}
 

- Executar o teste e ter certeza de que ele irá falhar, afinal nem existe a classe Calculadora ou o método soma() ainda!

- Criar a classe Calculadora e o método soma

 public int soma (int a, int b){
  return a+b;
 }

- Executar o teste novamente e ver que ele nao irá falhar dessa vez.

- Analisar o código escrito por completo e verificar se há necessecidade de refatoração

- Executar todo o código para fins de teste novamente para nao ter dúvidas de não haver falhas

- Repitir o processo com um teste diferente!


Desenvolvimento dirigido por testes tem uma grande utilidade ajudando
a manter baixa a contagem de defeitos e mantendo a simplicidade do código.
Outra vantagem é que o TDD quando usado apropriadamente, garante que todo o código desenvolvido seja inteiramente testado. Isto fornece
a equipe de desenvolvimento, e ao usuários um grande nível de confiança ao código.


No TDD, os testes são de extrema importância e por isso devem ser bem escritos.


Um exemplo de um código ruim de teste, utilizando o exemplo anterior da calculadora:


 public void TestaAdicao(){
  assertEquals(3+4,Calculadora.soma(3,4));
 }
 

Se estamos testando o resultado do metodo soma, então precisamos saber o retorno. Nesse caso, melhor
escrever o código da seguinte forma:


 public void TestaAdicao(){
  assertEquals(7,Calculadora.soma(3,4));
 }
 

alguns pontos que devemos lembrar:

- Conseguir executar cada teste isoladamente
- Eliminar qualquer duplicação lógica dos testes
- Cada teste deve testar apenas um negócio
- Testar todas as pré-condições, pós-condições e exceções
- É também importante sempre escolher bons nomes para os métodos de teste,mesmo que fiquem longos

Não há nenhuma comprovação científica de que TDD melhore a qualidade do código ou a produtividade dos desenvolvedores mas existem
algumas evidências, eos relatos de inúmeras pessoas não nos deixam pensar de outra forma!


Referências:
Refatorações para Padrões do Joshua Kerievsky
Wikipedia Test Driven Developmen : http://pt.wikipedia.org/wiki/Test_Driven_Development

Padrão de Projeto Iterator

Padrão Iterator

        Imagine que você trabalha em uma rede de supermercado e tenha que mostrar uma lista de produtos, Refrigerante e Bolachas recheadas. Ai você descobre que a lista de refrigerantes foi criada utilizando Arraylist e os bolachas com Matriz.

Como mostrar ambos produtos? Apesar de ambos serem produtos cada um tem uma forma diferente de implementação.

Você não quer ou não pode padronizar a lista de produtos. Como conseguir mostrar uma lista que contenha ambos produtos?

Bom, poderia fazer um for para cada um e mostrar o produto pelo loop. Assim:

ArrayList<Produto> arrayListDeProdutos = new ArrayList<Produto>();
for (Produto produto : arrayListDeProdutos ) {
 System.out.println(produto.nome);
}

Produto[] matrizDeProdutos = new Produto[3];
for (int i = 0; i < matrizDeProdutos .length; i++) {
 System.out.println(matrizDeProdutos [i].nome);
}


É fácil perceber o problema dessa implantação sem ver o código, caso outra equipe armazene algum produto com outro tipo de estrutura, seria necessário mais um for.
E se precisar fazer alguma operação com a lista de produtos, terá de haver o mesmo método repetido para cada lista com estrutura diferente.

Ok, então vamos ver agora uma boa solução para esse problema.

Vamos conhecer o Padrão Iterator:

Conceito:
O Fornecer uma maneira de acessar sequencialmente os elementos
de um objeto agregado(coleção ou grupo de objetos) sem expor sua implementação.

A interface tem o método hasNext() que determina se há mais elementos no grupo para serem submetidos a iteração.

O método next() retorna o próximo objeto do grupo.

Obs: Quando falamos em coleção estamos nos referindo simplesmente a um grupo de objetos, mesmo que estejam armazenados em estruturas de dados muito diferentes como lista, matrizes hastable eles continuam formando uma coleção de objetos.

As vezes essas coleções são chamadas de Agregados.

A ideia do padrão Iterator é fornecer um meio eficiente de se percorrer todos os elementos de uma determinada coleção e de formas variadas.

O padrão propõe que se utilize uma interface para controlar a iteração. O objeto que implementar esta interface conhece a seqüência do grupo e mantém uma referencia ao elemento atual de forma que possa ser utilizado de forma semelhante à iteração em um array.

O foco principal é em separar a responsabilidade de gerenciar o armazenamento dos dados da coleção, da forma como eles são percorridos.
  •   Um iterador simples poderia percorrer a lista em ordem sequencial.
  •   Um outro iterador poderia filtrar alguns dados para serem percorridos.
  •   Um outro ainda, poderia percorrer a sequência segundo algum critério de ordenação.

ESTRUTURA BASICA:



Então, respondendo ao exemplo anterior, ao utilizar o padrão iterator nós podemos acessar os elementos de um conjunto de dados sem conhecer como se deu sua implementação, não há a necessidade de se preocupar se ele foi estruturado com Arraylist ou Matriz.

O método da criação do iterator retorna um iterador de Lista que tem o ArrayList produtos como conjunto. Para a classe que utiliza matriz para armazenar os produtos esse método cria um Iterator que retorna um iterador de Matriz

Pronto, agora os diferentes conjuntos estão encapsulados numa interface comum. Agora falta implementar os iteradores.

A lista será mostrada pela Interface comum aos produtos criada para o Iterator.

A Interface do Iterador define um TIPO para acessar os elementos de uma coleção qualquer.
Ele está implementado com os métodos genéricos para percorre uma coleção:

  • boolean hasNext() : Determina se há um próximo elemento apontado pelo iterador
  • Object next(): Recupera o próximo elemento apontado pelo iterador  
  • void remove(): Remove o elemento recuperado pelo iterador

Como criar um Iterador?

Simples, importar ele e definir a coleção a ser iterada:
import java.util.ArrayList;
import java.util.Iterator;


Exemplo de Iteração:
import java.util.ArrayList;
import java.util.Iterator;
public class produtos {
 public static void main(String args[]) {
  ArrayList<String> produtos = new ArrayList<String>();
   //Adicionando elementos no array
   produtos.add("Coca-Cola");
   produtos.add("Pepsi");
   produtos.add("Sprite");
   produtos.add("Guarana");
   produtos.add("Sukita");
   produtos.add("Kuat");
   produtos.add("Fruki");
  
  //Criacao do Iterador
  Iterator<String> it = produtos.iterator();
  System.out.println("Tamanho da Lista antes da Iteração " + produtos.size());

  //Usando o Iterator pra checar se tem produtos
  while (it.hasNext()) {
   //Obtemos o proximo elemento
   String s = it.next();
   System.out.println(s);
   //Se nao for Coca-Cola, remove
   if (!s.equals("Coca-Cola")) {
    it.remove();
   }
  }
  System.out.println("\nTamanho da Lista depois da Iteração " +
  produtos.size());
  for(String s: produtos){
   System.out.println(s);
  }
 }
}


SAIDA DESSE EXEMPLO:
Tamanho da Lista antes da Iteração: 7
Coca-Cola
Pepsi
Sprite
Guarana
Sukita
Kuat
Fruki


Tamanho da Lista depois da Iteração: 1
Coca-Cola


Pode se ver neste exemplo uma característica interessante do Iterator, remover ao mesmo tempo que ocorre a iteração.

Vemos então que o Iterator tem uma grande aplicabilidade, como;
  •  Acessar conteúdo de uma coleção sem expor sua representação interna
  •  Suportar múltiplas plataformas de iteração
  •  Prover uma interface única para varrer coleções diferentes, isto é chamado de iteração polimórfica.
Referências:
Use a Cabeça! Padrões de Projeto de Eric & Elisabeth Freeman.
Google Books - Use a Cabeça ! Padrões de Projetos (design Patterns)

Padrão de Projeto Iterator em Java - Conceitos, Funcionamento e Aplicação prática, Devmedia:
http://www.devmedia.com.br/padrao-de-projeto-iterator-em-java-conceitos-funcionamento-e-aplicacao-pratica/28748

Sérgio Taborda’s Weblog por Sérgio Taborda, Iterator:
http://sergiotaborda.wordpress.com/desenvolvimento-de-software/java/patterns/iterator/



Marcos Vinicius Scholl:
Graduando em Tecnologia em Análise e Desenvolvimento de Sistemas - TADS 
Universidade Federal do Rio Grande - FURG 
Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Sul IFRS - Campus Rio Grande 
Rua Eng. Alfredo Huch, 475 - Rio Grande, RS - Brasil - 96201-460

Sobre o autor:
Marcos Vinicius Scholl está na internet desde o longínquo ano de 2002. Estuda na área de Tecnologia de Informação. É entusiasta na área de tecnologia. Gremista de paixão e anticolorado por prazer!

Métodos Nativos

Native Methods

Utilizando Métodos Nativos (Native Methods) em Java.

Métodos nos quais já foram implementados em C ou C++ e utilizados em aplicativos da mesma linguagem podem ser reutilizados nos aplicativos Java através de Métodos Nativos.

Usando a palavra chave "native" em um método de uma classe ela altera o compilado no qual o método será definido externamente, os métodos nativos como não tendo a implementação suas declarações são similares a métodos abstratos.

Ex:
class  OlaMundo{
    public native void ola();
}


Primeiramente essa classe deve ser compilado normalmente “javac OlaMundo.java” após deve ser utilizado a classe utilitária "javah" para criar um arquivo de leitura C da classe OlaMundo "javah OlaMundo" que pode ser achada no diretório jdk/bin.

Ex:
#include <jni.h="">
#ifndef  _Included_OlaMundo
#define _Included_OlaMundo

Extern “C” {

    /*
    *Class: OlaMundo
    *Method: ola
    *Sognature: ()V
    */

    JNIEXPORT void JNICALL Java_OlaMundo_ola (JNIRev *, jclass);

    #ifdef __cplusplus 
}

#endif
#endif


Dentro do arquivo esta a declaração da função Java_OlaMundo_ola, o JNIEXPORT e JNICALL são necessários para a utilização da biblioteca dinâmica.

A seguir a classe C deve ser implementada dessa forma, copiando a leitura do arquivo.

Ex:
#include "OlaMundo.h"
#include <stdio.h="">

JNIEXPORT void JNICALL Java_OlaMundo_ola (JNIRev * env, jclass cl);
{
    Print("Ola Mundo em JAVA utilizando o print de C!/n");
}


Deve-se carregar o biblioteca dinâmica porem a linha de comando muda dependendo do SO.

Ex:

Linux compilador Gnu:
-gcc -fPIC -I jdk/include/linux -shered -o libOlaMundo.so OlaMundo.c

Windows compilador Microsoft:
cl -I jdk\include -I jdk\include\win32 –LD OlaMundo.c –FeOlaMundo.dll


Por último deve ser chamado um método System.loadLibrary() para assegurar que a biblioteca será carregada antes do primeiro uso da classe.

Ex:
class TesteOlaMundo{
    public static void main (String[] args){
     OlaMundo.ola();
    }

    Static{
        System.loadLabrary("OlaMundo");
    }
} 


Referencia: Livro Core Java Volume II – Advanced Features do autor Gary Cornell

Introdução a Refatoração



Segundo o autor refatoração é o processo de alteração de um sistema de software de modo que o comportamento externo do código não mude, mas que sua estrutura interna seja melhorada.
 
Para que seja mais fácil entender vou utilizar o exemplo usado pelo autor. Um programa para calcular e imprimir o valor devido por um cliente em uma vídeo locadora. O programa calcula o valor devido, que depende de quanto tempo o filme foi alugado e identifica o tipo do filme. Há três tipos de filmes: normais, infantis e lançamentos.

Classes




Conforme o exemplo do autor os usuários do sistema gostariam que a conta fosse impressa em HTML, para que esta possa ser acessada pela WEB. Considerando o código atual teríamos que implementar um novo método para a conta em HTML, que duplicaria muito do comportamento do método conta existente, pois é impossível reutilizar o código do mesmo.

Para poupar trabalho poder ia-se copiar o código do método conta, e fazer alterações para adapta-lo ao método contaHml, porém está prática pode gerar uma inconsistência uma vez que as regras de cobrança podem mudar, assim o desenvolvedor teria que aplicar essas mudanças nos dois métodos.

O primeiro passo da refatorção é criar um sólido conjunto de testes, e executa-los antes de começar a alterar o código, para que se obtenham os resultados antes da alteração. A cada alteração concluída devem-se executar os mesmo testes para verificar se as funcionalidades permanecem iguais. 

Depois de criado os casos de testes, devemos decompor o método conta em partes menores, visto que este método é muito extenso e não pode ser reaproveitado. O autor faz muitas alterações na estrutura do código, mas vamos focar apenas no switch que obtém o valor de cada filme, então criamos um método que tenha esta funcionalidade, assim quando for criado o método contaHtml podemos utiliza-lo ao invés de copia-lo.



Após a alteração feita é importante testar para verificar se as funcionalidades permanecem integras, uma alteração importante neste método seria alterar o nome da variável cada para um nome mais intuitivo, então o autor chamou-a de umaLocação.





Como na maior parte dos casos, um método deve estar no objeto cujos dados ele usa. Assim, o método deve ser movido para a classe Locação.



Feito isso podemos utilizar este método tanto para gerar o valor de cada locação do método conta quanto do método contaHtml. Caso haja alguma alteração no cálculo do valor de cada locação, como por exemplo, inserir uma nova categoria de filme devemos então alterar apenas este método.


Referências

Refatoração:Aperfeiçoando o Projeto de Código Existente do Martin Fowler, Arquiteto de Software.

Padrões de Projeto

Bom, meu post vai ser sobre o livro Padrões de Projeto, dando uma breve explicação do que tem no livro: O livro Padrões de Projeto reúne um total de 23 padrões, que baseado em problemas nos fornece soluções que podemos utilizar em um projeto de software. O livro separa os padrões em 3 critérios: criação, estrutural e comportamental. Cada critério tem seus padrões relacionados. Os padrões também se aplicam quanto as classes e quanto aos objetos. Logo abaixo uma tabela que tem logo no inicio do livro:
 Criação            Estrutural     Comportamental
Classe Factory Method    Adapter(class)   Interpreter/template method

Objeto Abstract Factory   Adapter(object)  Chain of Responsibylity
 Builder            Bridge     Command
 Prototype    Composite     Iterator
 Singleton    Decorator     Mediator
             Façade     Memento
             Flyweight     Observer
             Proxy     State
                       Strategy
                       Visitor




Bom, passado a tabela da pra ter uma melhor ideia da divisão dos padrões e de suas aplicações. Como não tenho tempo de ler o livro todo, fui lendo sobre os padrões, achei interessante o Singleton,ele esta na pagina 130 do livro Padrões de Projeto.

Singleton é um Padrão de Projeto que nos garante que tenha somente uma instância de uma determinada classe, tendo um ponto de acesso global ao objeto na classe.O livro aborda como um exemplo de motivação uma impressora no sistema, onde temos muitas impressoras, mas teríamos que ter somente um spooler de impressão. Para obtermos somente uma instância da classe pode ser criado um método estático que chama o construtor da classe e verifica se não tem instância, caso não tenha retorna uma nova, caso tenha retorna a mesma. Um exemplo que achei em java:

public class MySingleton{
 
   //variável estativa que ira contes a instancia do método
   private static MySingleton instance;
 
   //contrutor do método
   private MySingleton(){
   }

   //metodo public estatico de acesso unico ao objeto
   public static MySingleton getInstance(){
 if(instance==Null){
    return new MySingleton
         }
   return instance;

  }
}

Visto no exemplo acima, não teríamos como criar uma instância da classe sendo o construtor privado, porem o método publico da classe é estático e esse pode ser invocado sem a necessidade de uma instância, assim resolvendo nosso problema,que ao chamar o método estático automaticamente se cria uma instância da classe, ou então devolve a mesma que já exista.

Outro padrão que achei interessante é o padrão Adapter, esse padrão tem como objetivo adaptar a interface de uma classe para que seja utilizado por outra classe, um exemplo seria um toolkit, onde temos uma classe que precisa ser utilizada em nossos sistemas, porem para fazer o encaixe da classe precisamos de um adaptador, para que possamos utilizar a biblioteca, uma das vantegens de usar esse padrão é vincular o cliente a uma interface separado e não diretamente na classe que vai ter suar interface adaptada, um exemplo de código em Java:

public class TomadaDeDoisPinos{
  public  void ligarNaTomadaDeDoisPinos(){
       System.out.println(“ligado na tomada de dois pinos”);
        }
}

public class TomadaDeTresPinos{
  public  void ligarNaTomadaDeTresPinos(){
      System.out.println(“ligado na tomada de tres pinos”);
        }
}

public class AdapterTomada  extends TomadaDeDoisPinos {
  private TomadaDeTresPinos  tomadaDeTresPinos;
   
 public AdapterTomada(TomadaDeTresPinos tomadaDeTresPinos){
  this.TomadaDeTresPinos = tomadaDeTresPinos;
        }

public  void LigarNaTomadaDeDoisPinos(){
        tomadaDeTresPinos. LigarNaTomadaDeDoisPinos;
        }


}


No exemplo de código acima temos uma classe AdapterTomada, quer ser ve como adaptador para interface das outras duas classes.Segue o exemplo do código em funcionamento:

public class Teste{
 public static void main(String argumentos[]){
  TomadaDeTresPinos  tomadaTresPinos = new  TomadaDeTresPinos;
  
  AdapterTomada adaptador  = new AdapterTomada(tomadaTresPinos);
  
  adaptador. LigarNaTomadaDeDoisPinos();
    }
}

Referências :
http://www.devmedia.com.br/padrao-de-projeto-adapter-em-java/26467
http://pt.wikipedia.org/wiki/Singleton
Livro Padrões de Projeto – Soluções Reutilizáveis de Software Orientado a Objetos

Bom , acho que vou concluir meu post por aqui, porque tem muitos padrões, tenho que ler com calma : ).

Autor:
Andrei Espelocim Garcia
Graduando em Tecnologia em Análise e Desenvolvimento de Sistemas- IFRS- CAMPUS Rio Grande

Introdução a Modelagem de Domínios

Neste post irei abordar uma introdução à modelagem de domínios, com algumas boas dicas e práticas, através de exemplos elaborados de forma simples em que o leitor entenda e compreenda a ideia proposta.

Todos os softwares ao serem projetados são relacionados a alguma atividade ou interesse do usuário. Este interesse em que o usuário aplica o programa é o Domínio do software.

Um domínio geralmente está ligado ao mundo físico, por exemplo, um programa de reservas de ingressos de jogos de futebol, envolve pessoas reais entrando em estádios de futebol reais. Alguns softwares são intangíveis, o domínio de um software bancário, por exemplo, envolve o dinheiro, finanças. Porém, existem exceções, como um software IDE(Integrated Development Environment), ou Ambiente integrado de desenvolvimento, que gerencia o próprio desenvolvimento de softwares.

Para criar softwares que desempenham as atividades de forma coerente para os usuários, a equipe de desenvolvimento ou o desenvolver, devem trazer consigo muitos conhecimentos sobre o domínio do projeto. A quantidade de conhecimentos sobre o assunto pode muitas vezes ser absurda, para isso existem os Modelos, que são uma forma de organizar e selecionar os conhecimentos de um domínio, com coerência para criar uma boa estrutura.

Um modelo não se trata de um diagrama específico, é apenas uma espécie de rascunho “sofisticado” de como virá a ser o diagrama final.

Observe, abaixo, uma modelagem de domínio de um software de caixa eletrônico bancário. O domínio, neste exemplo, se trata de clientes movimentando seu dinheiro. Realizando, assim, operações e movimentações de suas contas através do software.
O primeiro modelo elaborado com todas as informações poderia ser algo deste tipo:


Após um refinamento das informações e aplicação dos princípios de modelo, poderíamos melhorar o exemplo anterior e chegar a um novo, algo assim:


Este post apenas introduz a ideia de modelagem de domínios, este exemplo poderia ser mais refatorado, mas deixo o interesse de ir além aos leitores.

Para se aprofundar mais sobre este assunto, recomendo a leitura do livro referênciado.

Referências:
- EVANS, E. Domain-Driven Design: Tackling Complexity in the Heart of Software. New York: Addison-Wesley, 2004.
- BRIÃO, W, E. Trabalho de Programação Orientada a Objetos. Rio Grande: IFRS, 1°Sem/2013. Acesse neste link.

Enumerados

Antes de o tipo enum ser adicionado em Java, eram usados inteiros nomeados para fazer seu trabalho.
Ex:

public static final int MES_JANEIRO = 1;
public static final int MES_FEVEREIRO = 2;
public static final int MES_MARCO = 3;
//...

Porem usando esse desse tipo, ele tem muita deficiência (tipo multiplicar um pelo outro, igualar, e etc...) e também não existe uma maneira fácil de converter constantes enumeradas int em strings. Programas que usam esse tipo de padrão são muito frágeis.
A partir do Java 1.5 já esta incluso o tipo enum Java. Sua principal funcionalidade é agrupar valores de mesmo sentido e também deixar o código mais legível. No exemplo a seguir agrupamos os nomes dos meses dentro de um enum chamado Mês.

public  enum Mes {JANEIRO, FEVEREIRO, MARCO} //...

Assim se você declarar um parâmetro do tipo Mes do qual o objeto passado só poderá receber valores que estão em nosso enum.

Mes m = Mes.JANEIRO

// A VARIAVEL M RECEBERA JANEIRO

m = Mes.FEVEREIRO

// AGORA M RECEBEBERA FEVEREIRO

m = Mes.ALGUMACOISA

// RESULTA EM ERRO PORQUE "ALGUMACOISA" NÂO
//  É UM VALOR DO TIPO ENUMERADO MES

Tipos enum's são implicitamente final e suas constantes são static. São usadas com mais freqüência quando se precisar de um conjunto finito de constantes. No enum você também pode adicionar método e campos arbitrários, NUNCA são usados dentro de métodos.
O enumerado é muito semelhante a de uma classe, porém todos seus valores nunca poderão ser modificados. O construtor por exemplo é muito semelhante a de classe, só que sua usabilidade se da somente para indicar o que cada valor contido na constante significa.
Não é aconselhado usar o método “ordinal()” para retornar o valor da posição da constante. É muito melhor armazená-lo em um campo de instancia, pois caso algum dia você alterar seu código e mudar a seqüência das constantes não mudaria os valores delas, como aconteceria se usássemos o método ordinal.

public  enum Mes {
 JANEIRO(1),
 FEVEREIRO(2),
 MARCO(3);

 private final int numeromes;

 private Mes(int numeromes){
  this.numeromes=numeromes;
 }
}

Exemplo de uso de gets, construtor e toString(Override) :

public class Teste {
    public  enum Mes {
        JANEIRO("Janeiro",31),
        FEVEREIRO("Fevereiro",28),
        MARCO("Março",31);
        //...
        private final int dias;
        private final String nome;
        
        private Mes(String nome, int dias){
            this.nome = nome;
            this.dias = dias;
        }
        public int getDias(){
            return dias;
        }
        public String getNome(){
            return nome;
        }
        @Override
        public String toString(){
            return "Mes: "+nome+" tem "+dias+" dias.";
        }

    }
    
    public static void main(String[] args) {
       
        //PERCORRER VALORES
        for(Mes m  : Mes.values()) {
                System.out.println(m); // Automaticamente
                                       // chama m.toString()
        } 
    }
}

Referências:

Java Efetivo do Joshua Block, Arquiteto de Software na Google.

Blog Red Pill do Marcio Torres, Analista e Desenvolvedor de Sistemas/Professor no IFRS

Site Tiexpert, post do Denys William Xavier.

Tipos Genéricos (Generic Types)

Vamos imaginar que precisamos criar uma classe chamada Celular, mas nós não sabemos qual tipo de celular que iremos ter por que afinal existem vários. Ele pode ser de um chip, dois, três ou quatro chips, ser touch ou não e por aí vai. Então vamos criar as classes.

public class CelularUmChip {

    private String marca;
    private int numeroChip1;
   
    public CelularUmChip(String marca, int numeroChip1, 
            boolean ehTouch) {
        this.marca = marca;
        this.numeroChip1 = numeroChip1;
        this.ehTouch = ehTouch;
      
    }    
}

class CelularDoisChips {

    private String marca;
    private int numeroChip1, numeroChip2;    

    public CelularDoisChips(String marca,int numeroChip1, 
            int numeroChip2) {
        this.marca = marca;
        this.numeroChip1 = numeroChip1;
        this.numeroChip2 = numeroChip2;
    }
}

Deu de ver que o código vai ficar repetido né? É aí que utilizamos os Generics. Porém os Generics só vieram há ser implementados na versão 1.5 do Java, até então era necessário pegar os tipos como Object e fazer casting para qual o tipo desejado.
A classe Celular ficaria assim:
public class Celular{
 private Object cellphone;

 public Celular(Object cellphone){
  this.cellphone = cellphone;
 }
 
 public Object getCellphone(){
  return cellphone;
 }
}

E no Main seria necessário fazer o casting como representado no código abaixo.

public class Main{
 public static void main(String[] args){
  Celular c = new Celular(new CelularUmChip("Motorola", 81909090));
  
  CelularUmChip c1 = (CelularUmChip)c.getCellphone();
 }
}

Programando desta maneira seria muito fácil ocorrer erros de Casting, como o clássico "ClassCastException", que dispara quando estamos instanciando a classe errada.

Mas a partir do Java 1.5 já se pode usar os Generics. O que ele faz é instanciar uma classe para que ela receba qualquer tipo de classe ou qualquer tipo primitivo, como int, double e etc. Para instanciar uma classe Generics basta colocar ao lado do nome um <T>, assim já estamos dizendo ao compilador que ele irá receber tal classe quando estiver em compilação. O que vai entre <> não se limita somente ao “T”, há outros placeholders:


E – Element

K – Key

N – Number

T – Type

V – Value

Os Generics permitem o uso de mais de um placeholder no mesmo <>, como por exemplo:
Example <T, N, V>
Vamos refazer nosso código agora utilizando Generics.

public class Celular<T>{

    private T cellphone;

    public Celular(T cellphone) {
        this.cellphone = cellphone;
    }    

    public T getCellphone() {
        return cellphone;
    }    
}
Agora nossa classe Celular vai poder receber qualquer uma das outras que foram declaradas antes.
public class Main {
    public static void main(String[] args) {
        
      Celular<CelularUmChip> c = new Celular(new CelularUmChip("Motorola", 
              81909090));
      
        Celular<CelularDoisChips> c2 = new Celular(new CelularDoisChips("Nokia",
                90909090, 99999090));
    }
}

Referência:

Java Efetivo do Joshua Block, Arquiteto de Software na Google
Site Tiexpert, post do Denys William Xavier
Site Plugmaster, post do Dayvid Lima.

Tratamento de erros em java


Tratamento de erros pode parecer tão simples mas são muitos fatores a se considerar, a entrada pode estar errada o dispositivo pode falhar, é responsabilidade do programador certificar-se que o código não trave com esses erros. O tratamento de erro domina completamente muitos códigos fonte, ou seja, se torna praticamente impossível ver oque o código faz devido a tantos tratamentos espalhados pelo código. O tratamento é um recurso importante, mas se ele obscurecer a logica, está errado.

Uma coisa interessante sobre exceções com try-catch-finally é que elas definem um escopo dentro de seu programa, que ao executar a parte do try você declara que aquela execução pode ser cancelada a qualquer momento, e então, continuar no catch. Vejamos um exemplo. Precisamos criar um código que acesse um arquivo e consulte alguns objetos em série. Começamos com um teste de unidade que mostra como capturar uma exceção se o arquivo não existir.

@Test (expected = StorageException.class)
public void retrieveSectionShouldThrowOnInvalidFileName() {
	sectionStore.retrieveSection(“invalid – file”);
}

O teste nos leva a criar esse stub:

public List<recordedgrip> retrieveSection(String sectionName) {
	// retorno fictício até que tenhamos uma implementação real
	return new ArrayList<recordedgrip>();
  Nosso teste falha porque ele não lança uma exceção. Em seguida, mudamos nossa implementação de modo a tentar acessar um arquivo inválido. Essa operação lança uma exceção:

public List<recordedgrip> retrieveSection(String sectionName) {
	try {
		FileInputStream stream = new FileInputStream(sectionName)
	} catch (Exception e) {
	  throw new StorageException(“retrieval error”, e);
	}
	return new ArrayList<recordedgrip>();
}
Nosso teste funciona agora porque capturamos uma exceção. Neste momento podemos refatorar. Podemos reduzir o tipo de execução que capturamos para combinar com aquele que realmente é lançado pelo construtor FileInputStream: FileNotFoundException:

public List<recordedgrip> retrieveSection(String sectionName) {
	try {
		FileInputStream stream = new FileInputStream(sectionName);
		stream.close();
	} catch (FileNotFoundException e) {
	  throw new StorageException(“retrieval error”, e);
	}
	return new ArrayList<recordedgrip>();
}
 Agora que definimos o escopo try-catch, podemos construir o resto da lógica que precisamos, que será adicionada na criação do FileInputStream do close e poderá fingir que nada de errado aconteceu.

Não se esqueça de criar mensagens de erro informando a operação e tipo de falha que ocorreu, pois cada exceção lançada deve fornecer contexto suficiente para determinar a fonte e a localização de um erro.

Existem muitas formas de classificarmos erros seja pela origem: de onde vieram, ou pelo tipo: são falhas do dispositivo ou da rede ou de erros de programação. Geralmente, uma única classe de exceção esta bom para uma parte especifica do código. Use classes diferentes apenas se houver casos em que você queira capturar uma exceção e permitir que outra passe.

Se você retornar um null basicamente esta jogando o problema em cima dos seus chamadores, basta esquecer uma verificação de null para que o programa se perca. Pior do que retornar null é passar null como parâmetro. A menos que esteja trabalhando com uma API que esteja esperando por null.

Por: Bruno Lima.


BIBLIOGRAFIA [Martin]: Agile Software Development: Principles, Patterns, and Practices, Robert C. Martin, Prentice Hall, 2002.