quinta-feira, 7 de fevereiro de 2013

Mãos à obra!

Olá! Hoje resolvi escrever sobre um padrão de projeto muito interessante, chamado Builder, um dos padrões criados pelo GoF. 
Antes de falar do Builder especificamente, precisamos entender qual é a motivação para utilizar padrões de projetos. Provavelmente você já enfrentou um problema "cabeludo" e perdeu vários dias tentando resolvê-lo, enquanto se lamentava ao pensar que ninguém havia passado por este problema antes. Mas você estava enganado. Muito enganado. Provavelmente muitas pessoas já enfrentaram situações iguais ou muito parecidas que você já enfrentou, e algumas dessas pessoas ficaram tão frustadas quanto você ao perder horas (ou até dias) tentando resolve-las. Só que um grupo chamado GoF decidiu criar alguns padrões para resolver problemas cotidianos. Por que? Simplesmente para melhorar a vida das pessoas. Tornar o código mais limpo, mais elegante e de fácil manutenção. Quando aplicamos padrões de projeto em nosso código, estamos tornando-o muito mais profissional, e menos suscetível a erros.Geralmente é uma boa ideia a utilização dos padrões de projeto em nossos sistemas.
Mas vamos ao que interessa, que é o tal do Buider. Um código vale mais do que mil palavras, então vamos começar com o seguinte cenário:
public class Person {

  private String name;
  private int age;
  private String adress;
  private String lastName;
  private String phoneNumber;
  private String jobDescription;
  
  public Person(String name, int age, String adress, String lastName,
                String phoneNumber, String jobDescription) {
    this.name = name;
    this.age = age;
    this.address = address;
    this.lastName = lastName;
    this.phoneNumber = phoneNumber;
    this.jobDescription = jobDescription; 
  }
}
E nosso programa principal se pareceria com isso:
public class Main {
  public static void main(String[] args) {
     Person p = new Person("Paul", 32, "Street one",
         "MCcain","555-4444","Java Programmer");

     //do something                
  }
}
Nossa, quantos parâmetros! Eu realmente não quero lembrar de todos esses parâmetros ao instanciar um objeto Person, são muitos! Além do mais, é difícil ter que ficar verificando a ordem correta de cada um deles. É muito fácil, por exemplo, trocar o argumento "Street one" por "MCcain", o que causaria inconsistência no objeto Person, já que teríamos configurado o endereço como sendo seu sobrenome e seu sobrenome como o endereço de sua residência.
Será que não teria uma maneira mais "intuitiva" ao construir objetos Person? Felizmente, a resposta é SIM! O pessoal do GoF também achou muito chato isso e resolveu criar o padrão Builder, que traduzindo do inglês seria um "construtor". E sim, é ele que vai nos ajudar a criar novos objetos Person. Como? Veja abaixo:
public class Person {

  private final String name;
  private final int age;
  private final String address;
  private final String lastName;
  private final String phoneNumber;
  private final String jobDescription;
  
  private Person(Builder builder) {
      this.name = builder.name;
      this.age = builder.age;
      this.address = builder.address;
      this.lastName = builder.lastName;
      this.phoneNumber = builder.phoneNumber;
      this.jobDescription = builder.jobDescription;
  }

  public static class Builder {
    private String name;
    private int age;
    private String address;
    private String lastName;
    private String phoneNumber;
    private String jobDescription;
    
    //vamos exigir que o parâmetro obrigatório seja
    //somente o nome da pessoa
    public Builder(String name) {
      this.name = name;
    }

    public Builder age(int age) {
      this.age = age;
      return this;   
    }

    public Builder address(String address) {
      this.address = address;
      return this;
    }

    public Builder lastName(String lastName) {
        this.lastName = lastName;
        return this;
    }
    
    public Builder phoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }
    
    public Builder jobDescription(String jobDescription) {
        this.jobDescription = jobDescription;
        return this;
    }
    
    public Person build() {
        return new Person(this);
    }
  } 
}
E nossa classe principal ficaria assim:
public class Main {

    public static void main(String[] args) {
        Person p = new Person.Builder("Paul")
                .age(32)
                .address("Street 1")
                .jobDescription("Java Programmer")
                .lastName("MCcain")
                .phoneNumber("555-4444")
                .build();
    }
}
Olha que bacana. Ficou muito mais fácil e lógico instanciar um objeto Person, muito mais intuitivo, não acham? Perceba que o objeto Person é imutável neste caso. Não importa a ordem em que acessamos os parâmetros, eles sempre serão alocados da maneira correta. E lendo este código, fica muito mais fácil ver que estamos setando cada atributo individualmente, numa espécie de bind (recurso encontrado na linguagem Python, por exemplo). Por mais que tivemos que escrever mais código, o resultado final ficou muito melhor do que o primeiro exemplo. Mas daí vem a questão, vou ter que montar toda uma estrutura para resolver um problema aparentemente simples? Como usar um canhão para matar uma formiga? A questão é que seu software SEMPRE irá mudar, esteja preparado para isso. E quanto melhor estruturá-lo, menos dores de cabeça teremos ao precisar modificá-lo. Lembre-se, a única constante que temos no desenvolvimento de software é a palavra MUDANÇA. Outro fator é que provavelmente outras pessoas irão mexer em seu código, você quer mesmo que elas pensem que você não foi inteligente o suficiente por não utilizar um padrão de projeto, deixando seu código feio, sujo e difícil manutenção? É claro que não quer.

Bom, como tudo na vida é perfeito, vou listar também as desvantagens em utilizar o padrão Builder:

  1. Como o construtor de Person é private, a única forma de obter um Person é construir um builder antes;
  2. Classes com poucos atributos não justifica o uso do padrão. Mas lembre-se, mais tarde sua classe poderá mudar;
  3. Cuidar ao adicionar novos atributos, pois terá que adicioná-los tanto no Builder quanto na própria classe, e fazer a atribuição correta no construtor.
Tirem suas conclusões e vejam o que é melhor, não necessariamente essa é a única solução, mas é uma ótima solução caso queira "profissionalizar" seu código.
Bom, por hoje é só! Chega de Java por hoje, amanhã estarei indo para minha terra querida. Desejo à todos um ótimo feriado, pulem bastante Carnaval (mas com responsabilidade!).

Até a próxima! =)

quinta-feira, 31 de janeiro de 2013

Vivendo e aprendendo

Olá. No POST anterior, prometi falar um pouco sobre o Polimorfismo, porém ficará para uma próxima postagem, afinal, quando a gente está com "o fio da meada" na cabeça, é uma ótima oportunidade para transferir isso para um papel ou para um próprio blog! :) Desta forma então, hoje irei abordar um assunto de extrema importância: como trabalhar com a API JDBC do Java, mais especificamente, boas práticas em como utilizar tais recursos. No final do "artigo", há um estudo de caso. Provavelmente você já precisou fazer uma interface de conexão entre a sua aplicação e uma base de dados qualquer. Basicamente, não há mistério algum nisso. Um típico programa que se conecta com um banco de dados qualquer e executa alguns comandos simples se parece com isso:
1:  public class Foo {  
2:  public static void main(String[] args) {  
3:    Class.forName("oracle.jdbc.OracleDriver");    
4:    Connection c = DriverManager.getConnection(url, usuario, senha);  
5:    StringBuilder sql = new StringBuilder();  
6:    sql.append("select * from bar");  
7:    PreparedStatement stmt = c.preparedStatement(sql.toString());  
8:    try {  
9:     ResultSet rs = stmt.executeQuery();  
10:     while (rs.next) {  
11:      //faz bastante coisas aqui  
12:     }  
13:    } catch(SQLException e) {  
14:     e.printStackTrace();  
15:    }  
16:  }  
17:  }  
Como vimos, não há mistério nenhum nisso. Só que quando sua aplicação começar a "tomar forma", podemos tomar algumas ações para que seu código esteja protegido, que seja fácil de reutilizar e é claro, que não use recursos de forma aleatória e desnecessária.
Dica 1: Considere ter uma classe que lide com as particularidades de sua conexão. Conforme vemos no exemplo acima, expomos demais os detalhes de nosso banco de dados, como a URL de conexão, usuário e senha por exemplo, além de existir um complexo código para obter a conexão. O ideal seria criar uma classe ConnectionFactory que faria todo este trabalho para você, vamos ver como ficaria?
1:  public class ConnectionFactory {  
2:    private Connection conn;  
3:    private String driver;  
4:    private String usuario;  
5:    private String senha;  
6:    private String url;  
7:    public static ConnectionFactory conexao;  
8:    private ConnectionFactory() {  
9:     Class.forName(driver);  
10:     conn = DriverManager.getConnection(url,usuario,senha);     
11:     init();  
12:    }  
13:  public static getInstance() {  
14:     if (conexao == null) {  
15:       conexao = new ConnectionFactory();  
16:     }  
17:     return conexao;  
18:    }  
19:    private void init() {  
20:     //considere aqui ler um arquivo de propriedades que alimentará os 
        //campos necessários para a conexão  
21:    }  
22:  }  
E nossa classe principal ficaria assim:
1:  public static void main(String[] args) {   
2:    Connection c = ConnectionFactory.getInstance();  
3:    //restante do código omitido  
4:   }   
A propósito, nossa classe de conexão implementa um padrão de projeto chamado Singleton. Sugiro uma lida sobre Design Patterns.
Dica 2: Fique atento aos recursos de conexão! Como? Não podemos deixar recursos de conexão "largados" no servidor de banco de dados. Isso consome recursos do servidor, além de ocupar conexões desnecessárias. Se o limite de conexões simultâneas no seu servidor de banco de dados for relativamente baixo, isso pode ser um problema. E dos sérios. Até descobrir que o problema é na sua aplicação porque ela não faz a devida liberação de recursos, poderemos gastar horas e horas tentando achar o problema. (A propósito, quando as pessoas descobrirem isso, é melhor você correr - ou mudar de cidade). Nosso código ainda NÃO ESTÁ FAZENDO ISSO. Vamos ver como funciona?
1:  public static void main(String[] args) {   
2:  //código omitido  
3:    try {   
4:     ResultSet rs = stmt.executeQuery();   
5:     while (rs.next) {   
6:     //faz bastante coisas aqui   
7:     }   
8:    } catch(SQLException e) {   
9:     e.printStackTrace();   
10:    } finally {  
11:      rs.close();  
12:      stmt.close();  
13:      c.close();   
14:    }  
15:   }   
16:   }  
Veja o bloco finally em nosso código. Ele faz a liberação dos recursos sempre que o processamento terminar, nos livrando de vários problemas no futuro. Apesar de estarmos nos preocupando em fechar os recursos de conexão, ainda não estamos fazendo da forma correta. Mas como assim? O que está faltando? O bloco "finally" não garante que ele sempre será executado? Correto, mas não necessariamente tudo o que tiver no bloco finally será executado com sucesso!. Imagine a seguinte situação, se por algum motivo, na linha 11, rs.close() gere uma exceção. stmt.close() e c.close() jamais serão executados! Pense bem, a aplicação será abortada pois aconteceu alguma coisa estranha na chamada de rs.close(). Mas como então garantir de uma vez por todas que, mesmo que haja exceção em alguns desses comandos, o erro de um não influencie nos outros processamentos, evitando assim de fechar os recursos que já foram utilizados? A solução é claro, você já sabe: devemos executar cada close() em um bloco try...catch! Desta forma:
1:  public static void main(String[] args) {   
2:     //código omitido  
3:    try {   
4:        ResultSet rs = stmt.executeQuery();   
5:        while (rs.next) {   
6:           //faz bastante coisas aqui   
7:        }   
8:    } catch(SQLException e) {   
9:        e.printStackTrace();   
10:    } finally {  
11:        try {  
12:           rs.close();  
13:        } catch(SQLException e) {  
14:           //logar exceção  
15:        }  
16:        try {   
17:           stmt.close();   
18:        } catch(SQLException e) {  
19:           //logar exceção     
20:        }  
21:        try{  
22:           c.close();   
23:        } catch(SQLException e) {  
24:        //logar exceção  
25:        }  
26:    }  
27:   }  
Agora sim! Mesmo que aconteça alguma coisa errada com rs.close(), stmt.close() e c.close() ainda serão executados. O mesmo vale para stmt.close() ou c.close().
Dica 3: Tenha CERTEZA que os cursores criados durante o processamento de suas SQL's foram corretamente encerrados! Ahn? Como assim? Para cada chamada que o ResultSet recebe de stmt.executeQuery(), é criado um cursor implícito no servidor de banco de dados. Só que há um limite de cursores que podem ficar abertos. Quando esse limite é atingido, o servidor lança um erro dizendo que não pode mais lidar com tantos cursores abertos, te dá uma mensagem feia de erro e sua aplicação é interrompida. Esse erro é muito complicado de se resolver, dependendo do tamanho do seu projeto, pois você tem que se certificar em todos os lugares que faz conexão/manipulação com o servidor de banco de dados se seus recursos estão sendo devidamente encerrados. Isso tudo é muito legal, mas COMO garantir que os cursores serão devidamente encerrados? E é aí que entra um pouco de experiência da linguagem. O comando abaixo NÃO ENCERRA O CURSOR:
11:    try {  
12:           rs.close();  
13:        } catch(SQLException e) {  
14:           //logar exceção  
15:        } 
Mas isso aqui encerra:
11:    try {  
12:           rs.getStatement().close();  
13:        } catch(SQLException e) {  
14:           //logar exceção  
15:        } 
Veja a jogada! rs.getStatement().close(). É desta forma que você garante o encerramento dos cursores. Se formos analisar pela lógica da coisa, faz todo sentido. Quando chamamos rs.close(), estamos dizendo para o Java que não queremos mais trabalhar com o conjunto de resultados retornados pelo ResultSet. Ao fazer rs.getStatement().close(), estamos dizendo que queremos que aquela instrução executada seja "finalizada", ou seja, o cursor responsável por trazer a TUPLA para o ResultSet não necessita mais ficar aberta. Deu para entender? :) Estudo de caso Como eu mencionei no início do "artigo", vou compartilhar um estudo de caso que enfrentei por dois dias (sim, dois dias). Estou trabalhando com Oracle em um processo que envolve vários registros ao mesmo tempo, além de existirem conexões com duas bases de dados simultâneas. Ao trazer os registros para a memória, o Oracle me apresentava o seguinte erro: ORA-01000: maximum open cursors exceeded Ora, mas eu estava (aparentemente) fechando todos os meus recursos, como exemplifiquei aqui. Porém eu não tinha me tocado do rs.getStatement().close, sempre insistindo em apenas fechar o ResultSet. Foi só aplicar uma mudança de lógica do negócio e chamar rs.getStatement().close e pronto! Problema resolvido. Mas fiquei horas e horas trabalhando nisso, quando poderia estar implementando outras partes de minha aplicação. Então aí vai uma dica, cuidado! Não perca tempo como eu e já faça seu código livre desse erro maldito! :) Ufa! Por hoje é só, acabei me estendendo mais do que deveria, mas era isso que queria apresentar aqui hoje. Até a próxima!

segunda-feira, 21 de janeiro de 2013

Seja coeso e desligue-se um pouco :)

Hoje irei falar um pouco sobre coesão e baixo acoplamento. 
Primeiramente, o que significam esses termos?

1) Coesão: no Português claro, coesão significa que todo um contexto deve ter algum tipo de relação, uma harmonia. Se estivermos fazendo uma frase de um determinado assunto por exemplo, esta frase deve conter somente elementos pertinentes à aquele assunto, e não uma bagunça de outras coisas. No mundo da Informática, isso não é muito diferente. Devemos ter nossas estruturas coesas para evitar a complexidade dos nossos códigos. Vamos ver um exemplo na prática:

public class Impressora {
   public void imprimir() {
      conectarBancoDeDados();
      Documento documento = Documento.getDocumentosNaFila();

      System.out.println("Imprimindo documentos pendentes...");
      //Faz a comunicação com hardware para enviar os documentos para a impressora configurada. 
   } 

   public void conectarBancoDeDados() {
      Class.forName("oracle.sql.Driver");
      Connection conn = DriverManager.getConnection(urlBanco, usuario, senha); 
      PreparedStatement pstmt = conn.prepareStatement("select * from usuario");
      
      ResultSet rs = pstmt.executeQuery();
      //faz outras coisas complexas aqui  
   }
} 

Bom, ao ler esse código, claramente percebemos que a classe Impressora cuida de muitas coisas que ela não deveria cuidar, como podemos ver no método "conectarBancoDeDados". Analisando essa lógica, será mesmo que uma impressora deveria se conectar com um banco de dados? É aí que entra a coesão! Esta classe não está coesa, pois está executando tarefas além de sua responsabilidade. Devemos ter nossas estruturas fazerem o que é da alçada de cada um. Uma solução para esse exemplo seria mais ou menos desta forma:

public class Impressora {
   public void imprimir(Documento documento) {
      System.out.println("Imprimindo documentos pendentes...");
      //Faz a comunicação com hardware para enviar os documentos para a impressora configurada. 
   }
} 

Veja que retiramos a conexão com o banco na classe impressora, e que o documento que irá ser impresso já vem certo no método imprimir. Agora sim temos uma classe coesa, que faz somente o que foi projetada para fazer.

O papo está muito bom, mas e quanto ao baixo acoplamento? Nada como um exemplo prático:

public class Impressora {
   public void imprimir(Documento documento) {
      int numeroDePaginas = documento.numeroDePaginas;
      List<Autor> autores = new ArrayList<Autor>();
      for (Autor a : documento.autores) {        
         autores.add(a);  
      }
      System.out.println("Imprimindo documentos pendentes...");
      //Faz a comunicação com hardware para enviar os documentos para a impressora configurada. 
   }
}

Veja como conhecemos profundamente a classe Documento. E isso não é bom. Sabemos detalhes sobre esta classe, ou seja, a classe Impressora e Documento estão fortemente acopladas, consegue perceber?
Este tipo de coisa deve ser evitado, pois compromete toda uma estrutura. Imagine que alguém, em um belo dia, decide por algum motivo mudar a estrutura de armazenamento dos autores do documento, onde ao invés de List, irá armazenar numa estrutura Set. Nosso código do método imprimir() já estaria furado, pois teríamos que adequá-lo à nova estrutura, abrindo brechas para erros que antes não aconteciam. Encapsule o máximo de coisas que você puder, assim evitará grandes dores de cabeça!

Não sabe o que é encapsulamento? Então aguarde a próxima postagem, onde terei o maior prazer em tentar explicar.

Até logo. 

terça-feira, 15 de janeiro de 2013

Valores pré definidos no seu código? Utilize Enum!

Olá pessoal.

Hoje resolvi falar de um recurso muito interessante mas que por vezes passa despercebido em nossos códigos. Se você é um desses que armazena valores pré determinados em suas classes, este é o seu lugar: largue essa vida e utilize tipos enumerados.
Mas como é isso? Bom, vamos a um exemplo simples. Digamos que você esteja trabalhando com um aplicativo que controle uma lanchonete. Dentre os vários produtos que o seu estabelecimento oferece, o que mais se destaca são os deliciosos milk shakes de morango, que são vendidos em três tamanhos diferentes: pequeno (300ml), médio (500ml) e grande (enormes 700ml). No seu código, você tem algo parecido com isso:

public void realizarPedido(String tamanho) {
   if (tamanho.equals("pequeno")) {
      ml = 300;
   } else if (tamanho.equals("medio") {
      ml = 500;
   } else {
      ml = 700;
   }
}

Mas aí você me fala, "essa não é uma abordagem tão ruim assim, só tenho três if's aninhados!".
Bom, até hoje não deu problema algum, mas sua lanchonete está vendendo tantos milk shake's de morango que você decidiu fazer um tamanho "especial", de 1 litro! Bom, então o código acima, modificado, ficaria assim:


public void realizarPedido(String tamanho) {
   if (tamanho.equals("pequeno")) {
      ml = 300;
   } else if (tamanho.equals("medio") {
      ml = 500;
   } else if (tamanho.equals("grande")) {
      ml = 700;
   } else {
      ml = 1L;
   }
}


E assim seu código vai ficando mais complexo, com mais if's aninhados, sujo, e de difícil manutenção. Qual seria uma solução adequada para este caso? Poderíamos simplesmente criar uma estrutura que contemplasse o tamanho e a quantidade de cada milk shake! Mas não uma classe, e sim um Enum! Veja o exemplo abaixo:

public Enum TipoMilkShake {
   PEQUENO(300), MEDIO(500), GRANDE(700), GIGANTE(1000);

   public int tamanho;
 
   public int getTamanho() {
      return tamanho;
   }

   public TipoMilkShake(int tamanho) {
      this.tamanho = tamanho;
   }
}

Mas qual a vantagem disso? Vejamos agora nosso novo e melhorado método "realizarPedido":
public void realizarPedido(TipoMilkShake tipo) {
   ml = tipo.getTamanho();
 
   System.out.println("Você pediu um milk shake " + tipo);
}

Vejam só, a complexidade do nosso código se reduziu a apenas duas linhas! Agora eu não preciso mais ficar testando qual é o tipo de milk shake passado para determinar a quantidade de ML, pois o Enum já sabe disso. E quando eu precisar adicionar mais tamanhos ao meu cardápio, sem problemas, eu só vou mexer no tipo Enumerado, o restante do código automaticamente já estará pronto e funcionando para o novo tamanho!

Resumindo: este foi um exemplo bem simples, mas existem situações em que você vai perceber a real necessidade em utilizar tipos enumerados. Lembrando que eles são aceitos em testes condicionais switch/case também.

Por hoje é só, até mais e um abraço!

segunda-feira, 17 de dezembro de 2012

Então você quer programar em Java?

Boa noite!
Hoje venho falar de um assunto muito importante mas que passa despercebido pela maioria dos programadores Java (até mesmo os mais experientes!).
Todos sabemos que é um saco encontrar um código bagunçado e mal formatado, com vários "if's" infinitamente aninhados, laços mal identados e variáveis com nomes ridiculamente estranhos. E poderia ficar aqui acrescentando tudo de ruim que poderemos encontrar em um código mal formatado e fora dos PADRÕES.
Bom, mas o que esse cara tá falando? Você que se considera um programador Java de verdade, com certeza já sabe. As Convenções de Código em Java estão aí para nos dizer qual é o melhor caminho para formatarmos o nosso código, desde declaração de variáveis até nome de métodos. Você sabe, tudo que segue um padrão é mais fácil e rápido de olhar e dar manutenção.

Vejamos dois fragmentos de código, que por mais que sejam simples, dão uma diferença tremenda quando estamos falando de duzentas ou quinhentas linhas:


if (a == b && c == d)  // EVITAR!
if ((a == b) && (c == d)) // CORRETO


Segundo a documentação, é uma boa prática usar parênteses em operações que envolvem vários operadores para evitarmos problemas de precedência, mesmo que a precedência esteja clara para VOCÊ, pode não parecer tão clara assim para as outras pessoas - Java Code Conventions, 10.5 Miscellaneous Practices.

Além desse exemplo simples, as convenções de código em Java nos mostram como devemos programar a nossa API para que terceiros possam fazer uso dela. Você já viu como é intuitivo achar os métodos das classes em Java? Pois elas seguem exatamente esse padrão. E nada melhor do que seguir a recomendação que os próprios CRIADORES da linguagem elaboraram.
Pense nisso, um código limpo e fácil de entender significa qualidade!

Por hoje é só pessoal, um abraço e até a próxima!

quinta-feira, 13 de dezembro de 2012

Por que é tão difícil manipular esses benditos objetos?

Boa noite galera!
Estou estreando meu humilde blog com o objetivo de colocar algumas dicas e problemas cotidianos na vida deste simples programador (se você está procurando um blog de auto ajuda ou desabafos, aqui não é o seu lugar).

Para  começar com o pé direito (ou não), resolvi dedicar este primeiro POST à incrível dificuldade em se trabalhar com datas, seja em qualquer âmbito, bancos de dados, linguagens de programação e por aí vai.
Como sou entusiasta Java e já sofri (muito) com esses malditos objetos de data, hoje tirei a noite para desvendá-los de vez e quem sabe diminuir meu ódio por eles. E sabe que funcionou?

Não falando ainda de API de terceiros para manipular datas, se olharmos com carinho para a API nativa do Java para trabalhar como tal, temos as seguintes classes:

  • java.util.Date: Representa uma data e hora/mutáveis, ou seja, poderemos criar um objeto date simplesmente desta forma: Date data = new Date() e posteriormente realizar operações na data obtida (porém com outros objetos que serão listados a seguir);
  • java.util.Calendar: Esta poderosa classe nos permite diversas operações com data, desde acrescentar meses, dias, etc, até descobrirmos em que dia da semana será 22/01/2030;
  • java.text.DateFormat: Esta classe é usada para nos ajudar com a formatação de datas para todos os locais do mundo, ou seja, dependendo do local que a sua aplicação tiver rodando, poderá apresentar a data de acordo com o local;
  • java.util.Locale: É a classe que permite trabalhar com todos os locais do mundo. Ela é utilizada em conjunto com Calendar e DateFormat. Podemos especificar por exemplo, que estamos trabalhando com formato de data/hora do Japão (mais sobre isso a seguir).
Aqui vai um pequeno trecho de código que mostra o que já foi falado:


//Criando dois Locale's: um para o Japão e um para Portugal
Locale locJapan = new Locale("ja", "JP");
Locale locPortuguese = new Locale("pt","PT");
       
//Criando dois Calendar passando os locais especificados
Calendar dataJapao = Calendar.getInstance(locJapan);
Calendar dataPortugal = Calendar.getInstance(locPortuguese);
       
//transformando os objetos Calendar em Date para que possamos formatá-los e apresentá-los na tela
Date dataJapaoEmDate = dataJapao.getTime();
Date dataPortugalEmDate = dataPortugal.getTime();
       
//aqui utilizamos o objeto DateFormat para especificarmos o formato desejado e o local
DateFormat dfJapao = DateFormat.getDateInstance(DateFormat.FULL,locJapan);
DateFormat dfPortugal = DateFormat.getDateInstance(DateFormat.FULL, locPortuguese);
       
//Obtendo um String com as datas formatadas
String dataFormatadaJapao = dfJapao.format(dataJapaoEmDate);
String dataFormatadaPortugal = dfPortugal.format(dataPortugalEmDate);
       
System.out.println("Data no formato Japonês: " + dataFormatadaJapao.toString());
System.out.println("Data no formato Português (Portugal): " + dataFormatadaPortugal.toString());


O resultado dessa "bagunça" é a seguinte:
Data no formato Japonês: 2012年12月13日
Data no formato Português (Portugal): Quinta-feira, 13 de Dezembro de 2012

Vejam só que bacana! E não é que a coisa funciona mesmo? Prometo odiar menos as queridas classes básicas da API do Java :)
Porém, fui muito superficial ao explicar o código. Como eu sei que havia um tal de DateFormat.FULL, ou ainda, como eu sei os códigos passados no construtor de Locale para o Japão e Portugal?
Bom, quanto a primeira pergunta, a classe DateFormat possui várias constantes que nos permitem escolher o formato desejado. Elas representam inteiros e o nome delas fica bem fácil de usar. No exemplo acima, foi utilizado FULL porque desejei a data no seu formato completo (dia, mês e ano - não necessariamente nessa ordem, vai variar de acordo com o local). Quanto a segunda pergunta, há referências na Internet para sabermos quais códigos podemos passar para o Locale, sendo lista de linguagens e lista e código de países.

Mas nem tudo são flores. Quem já precisou fazer operações de verdade com datas (saber a diferença de um período ou de horas) sabe que é praticamente impossível trabalhar com a API nativa do Java. Mas calma, há uma solução muito elegante e funciona que é uma beleza, a Joda Time - Java date and time API que permite fazer DIVERSAS operações complexas com datas e horas!

Bom, o exemplo mostra que se lermos com carinho a documentação (de verdade) podemos sim "fazer miséria" com datas.

Por hoje é só, obrigado e até a próxima.