quarta-feira, 28 de novembro de 2012

Dicas para melhorar a qualidade do seu código

1. Mantenha o nome dos métodos o mais curto possível;

Os métodos quais estejam longos demais ao ponto que seja necessário utilizar a rolagem da tela necessitam de maior concentração e provavelmente o programador tenha dificuldades em ver o todo. De 5 a 20 linhas é um bom tamanho para na maioria dos casos.

2. Nem cogite a possibilidade de utilizar a mesma variável para mais de um propósito;

uma variável deve ser utilizar para apenas um propósito. Ao transformar seu comportamento com o modificador "final", podemos ajudar o compilador a otimizar o código, bem como deixar claro que o seu valor não vai mudar. É uma boa prática que deixa o código mais legível.

3. Procure nomear métodos e variáveis de forma clara e objetiva (anti-burro);

Qualquer um deve ser capaz de entender o código enquanto estiver olhando para ele. Evite ao máximo usar abreviações.

4. Declare a variável o mais próximo possível de onde serão utilizadas;

Normalmente ao fazer algum trabalho manual, posicionamos as ferramentas o mais próximo possível de nós! O mesmo deve acontecer com uma variável, para que não se perca o contexto de sua utilização em mais longos (com aproximadamente 20 linhas).

5. Não utilize números mágicos;

prefira sempre criar constantes ao comparar um valor variável com outro constante. Nada pior do que se deparar com o seguinte código durante a resolução de um bug:
va < 100
Acredito que seria mais fácil caso a linha acima fosse escrita da seguinte forma:
velocidadeAtual < VELOCIDADE_MAXIMA_PERMITIDA
6. Respeite as características da linguagem que esteja utilizando;

Digamos que você seja um programador Java que esteja aprendendo Ruby. Procure entender como fazer algo na nova linguagem, ao invés de tentar "portar" seu código escrito em Java. Normalmente a mesma coisa é feita de forma diferente em cada linguagem, conforme o exemplo a abaixo

em java:
for (int i = 0; i &lt; 5; i++) {

   System.out.println("Hello world!");

}

portado para o Ruby:
for i in (0..5)

   puts "Hello world!"

end
Como deveria ser em Ruby:
5.times { puts "Hello world!"}
7. Não vá contra os padrões de codificação;

Muitas linguagens possuem padrões de codificação, sendo a mais conhecida, a do Java. Quebrar quaisquer regras deste padrão só pode ser feita caso exista, realmente, uma boa razão para tal. Não o faça por simplesmente não gostar de algo.

8. Não otimize código antes da hora;

Em primeiro lugar, escreva código bem legível e totalmente coberto por testes unitários. Não se preocupe caso não seja executado na velocidade desejada. Faça o "refactory" a medida for necessário. Se realmente for necessária qualquer otimização, em primeiro lugar ache o ponto correto, para tanto, use ferramentas "Profiler". Evite resolver problemas que não existem.

9. Sempre faça o "refactory" seu código após testá-lo;

Uma forma de melhorar a qualidade do código e pelo "refactoring", após a bateria de testes unitários. Utilize as melhores práticas de TDD (Desenvolvimento Orientado a Testes) durante o ciclo de desenvolvimento de uma funcionalidade. Assim que o código passe em todos os testes, melhore seu código e o deixe o mais legível possível, sempre executado toda a bateria de testes para assegurar que nada quebrou durante o processo.

10. não seja bloqueado pelo "overdesign";

Padrões de Desenho (Design Patterns) devem ser utilizados para simplificar o desenho da solução, não significa que podem ser utilizados em todos os lugares e em todas as situações. Entenda primeiro a necessidade, construa a solução utilizando as melhores práticas de TDD e aí sim, melhorar aplicando o padrão correto, quando possível.

11. aprenda algo novo com prototipação.

Programação envolve o aprendizado de coisas novas. Quando se aprende uma nova biblioteca ou linguagem, geralmente surge a vontade e jogar fora todo o código antigo e aí sim, escrever tudo novo do zero. Definitivamente não é uma bom a fazer.

Adicionar a nova biblioteca ou linguagem a uma aplicação já existente pode causar o mesmo problema. Normalmente não é bom trocar todas as funções escritas em Javascript pelo JQuery, enquanto você ainda está aprendendo como ele funciona.

A melhor forma é criar projetos de exemplo, onde serão resolvidos os mesmos problemas com as novas bibliotecas ou linguagens. Estude, crie um protótipo e quando tiver a real noção do que pode ou o que não pode ser feito com elas. Daí sim, as utilize nos seus projetos.

Recomendo fortemente a leitura dos livros Code Complete, e Clean Code, cujos autores Steve McConnell do Robert C. Martin respectivamente.

Até a próxima! =]

sexta-feira, 9 de novembro de 2012

[VRaptor] Melhorando Hash MD5 com Salt

Vamos utilizar uma técnica chamada Salt, para adicionar melhor segurança à estratégia de guardar o hash das senhas no banco.

Esta técnica consiste em atribuir um valor aleatório, por cadastro, para que somado a senha, gere um hash.

O objetivo é impedir que existam senhas fracas no banco, suscetíveis a ataques de força bruta baseados em dicionários.

Alguns sites undergrounds oferecem dicionários com mais de 8 bilhões de entradas. O Linkedin sofreu um ataque desta natureza há algum tempo e como resultado, vazaram cerca de 6 milhões de senhas.

Espero que você goste:



O salt pode ser modificado de tempos em tempos, automaticamente, atualizando os hashes. E pode ser mantido em um local diferente. Você ainda pode guardar o salt cifrado em um algoritmo diferente.

Outras estratégias podem auxiliar a sofisticar mais ainda o processo, como gerar o hash do hash com diferentes algoritmos em várias iterações e por aí vai...

OBS: Esta técnica ajuda defesas contra ataques no banco de dados. Não elimina a necessidade da adoção de senhas fortes, uma vez que existem ataques direto a formulários e que exploram as fragilidades do protocolo http.

Espero ter te ajudado, apreciarei todas as críticas, dúvidas e sugestões.

Forte abraço,

Mauricio da Silva Marinho.

quinta-feira, 8 de novembro de 2012

[Java] Thread X Runnable

Threads está entre os assuntos mais difíceis do exame SCJP/OCJP. O que podemos dizer sobre elas...

Basicamente, temos dois tipos de ambiente de execução (em se tratando de execução):

  • Execução de segmentação única. Onde as ações são executadas em fila, uma após a outra, necessariamente. 
  • Execução concorrente. Onde várias ações disputam a atenção dos recursos de forma independente.

Neste último caso, em Java, as ações concorrentes são threads concorrentes. É o que chamamos de threads de execução. Dispomos deste recurso quando precisamos que alguma ação ocorra independente de outra(s), quando não podemos ficar esperando uma ação terminar para continuar executando uma tarefa ou uma sequencia de tarefas.

Em Java, uma thread é uma instância de java.lang.Thread. As execuções de aplicações Java, ocorrem em pilhas de chamadas. Cada pilha de chamadas reside em uma única Thread. Logo, ao criar uma Thread, estamos criando uma nova pilha de chamadas, no mesmo processo.

Um processo é um conjunto de tarefas, que em um SO multitarefa, pode executá-las concorrencialmente. Exatamente, as Threads!

Como a VM tratará a execução das threads dependerá da implementação da VM. Isso mesmo! Não espere garantir que uma aplicação que crie várias instâncias de Thread tenha o mesmo comportamento em VMs diferentes. Então, se este for o caso, não se pode basear a implementação em uma VM específica, a menos que saiba que a aplicação só será executada naquela VM.

A classe java.lang.Thread expõe alguns métodos para gerenciar as threads:

  • start()
  • yield()
  • sleep()
  • run() - chame aqui o código que será executado pela thread.
Cria-se uma Thread de duas formas:
  • Estendendo java.lang.Thread, ou
  • implementando java.lang.Runnable.

Estender Thread pode parecer mais fácil, mas certamente não é a solução mais elegante!

Por que? Simplesmente pelo fato de que a herança, em OO, é um recurso para se especializar uma classe, para que se faça algo novo.

Então se você precisa apenas criar uma execução em uma thread separada, não há necessidade de especializar Thread. Lembre-se que ao estender Thread, não poderá estender mais nada! Neste caso, o mais lógico, seguindo princípios de OO, seria implementar Runnable.

Implementar Runnable vai permitir que sua classe estenda outra classe de negócio, além de dispor dos comportamentos de Thread (ou de Runnable. Thread implementa Runnable, sabia?!).

Dizemos que estender Thread é mais simples porque por este caminho, para criar uma nova thread, basta instanciar a subclasse de Thread que você criou:

public class minhaThread extends Thread{
  public void run(){
    //Chama a execucao da nova thread aqui.
  }
}

MinhaThread novaThread = new MinhaThread();

Implementando Runnable (recomendável na esmagadora maioria dos casos), ficaria assim:

//Cria a classe que implementa Runnable
public class MinhaRunnable implements Runnable{
  public void run(){
    //Chama a execucao da nova thread aqui.
  }
}

//Instancia MinhaRunnable
MinhaRunnable minhaRunnable = new MinhaRunnable();

//Cria uma instancia de Thread, passando sua Runnable para executar
Thread novaThread = new Thread(minhaRunnable);

A classe Runnable que passamos para a Thread, tecnicamente chama-se destino (target).

Até o momento, a thread foi criada, mas não foi executada, dizemos que o estado dela é new. Para executar a thread, basta chamar .start() e a thread passará de new para running:

//Para iniciar a thread
novaThread.start();

Sacou?

Espero ter te ajudado. Críticas, dúvidas e sugestões são bem vindas.

Forte abraço e até o próximo post.

Mauricio da Silva Marinho

terça-feira, 6 de novembro de 2012

[VRaptor] Configurando o Hibernate

Olá, mais uma vez.

Neste vídeo tento explicar como configurar o Hibernate no VRaptor, para que ele (o VRaptor) gerencie a injeção das sessões do Hibernate nos DAOs. O projeto utilizado é o kanban, projeto criado no post anterior.
 
 Espero que este tutorial e suas explicações lhe sejam úteis de alguma forma. Apreciarei qualquer crítica e/ou sugestão.

 Forte abraço, Mauricio da Silva Marinho

sábado, 3 de novembro de 2012

[VRaptor] vraptor-blank-project no NetBeans

Importar o projeto em branco do vraptor (vraptor_blank_project) pode ser um pouquinho problemático pelo fato de os arquivos de propriedades do projeto para o NetBeans não estarem atualizados com o projeto. Veja neste vídeo explicativo como fazê-lo, inclusive, mudando o nome do projeto sem muitos problemas.


Espero que gostem, pois é o primeiro vídeo de muitos. Estou aprendendo a editar, os próximos serão melhores.

Forte abraço,

Mauricio da Silva Marinho

sexta-feira, 2 de novembro de 2012

[Java] Conversão de Tipos (Cast)

Pode parecer um assunto batido, mas será que não tem uns "peguinhas" para quem está de olho no exame para certificação?

Convertendo variáveis de referência.

Este assunto tem uma relação com polimorfismo. A mágica do polimorfismo está em você poder referenciar um tipo mais específico com uma variável de um tipo mais genérico.

Isto é possível porque o compilador verifica a árvore de herança das classes envolvidas neste processo, veja:

public class Veiculo{
  public void informaLotacao(){
     System.out.println("Um motorista.");
  }
}

public class Ferrari extends Veiculo{
  public void informaLotacao(){
    System.out.println("Um motorista e um passageiro.");
  }
  public void informaPotencia(){
    System.out.println("460 cavalos.");
  }
}

public class Onibus extends Veiculo{
  public void informaLotacao(){
    System.out.println("Um motorista e trinta e dois passageiros.");
  }
  public void informaCombustivel(){
    System.out.println("Diesel.");
  }
}

public class TestaVeiculo{
  public static void main(String[] args){
    Veiculo ferrari = new Ferrari();
    Veiculo onibus = new Onibus();
    System.out.println("Uma ferrari tem espaço para " + ferrari.informaLotacao() + " e possui " + ferrari.informaPotencia());
    System.out.println("Um ônibus tem espaco para " + onibus.informaLotacao() + " e funciona com " + onibus.informaCombustivel());
  }
}

Ao executar TestaVeiculo que saída você espera receber?

Na verdade, vai acontecer um erro de compilação dizendo cannot find symbol. Isso porque as referências são do tipo Veiculo, que não possui o metodo informaPotencia() e nem informaCombustivel().

Entenda que você só pode chamar os métodos que o tipo da sua variável de referência expõe para você, mesmo que seja uma referência para uma instância de um tipo mais especializado, com mais métodos.

Para acessar os métodos do tipo especializado temos primeiro que converter os tipos, veja:

public class TestaVeiculo{
  public static void main(String[] args){
    Veiculo ferrari = new Ferrari();
    Veiculo onibus = new Onibus();
    Ferrari ferrariConvertida = (Ferrari) ferrari;
    Onibus onibusConvertido = (Onibus) onibus;
    System.out.println("Uma ferrari tem espaço para " + ferrariConvertida.informaLotacao() + " e possui " + ferrariConvertida.informaPotencia());
    System.out.println("Um ônibus tem espaco para " + onibusConvertido.informaLotacao() + " e funciona com " + onibusConvertido.informaCombustivel());
  }
}

Agora você fez as pazes com o compilador, realizando o cast (conversão) de sua referência generica para um tipo mais específico, que expõe os métodos que você quer usar. Tudo certo! Mas pense comigo: já que o compilador olha apenas para a árvore de herança, o que aconteçe em uma situação assim:

public class TestaVeiculo{
  public static void main(String[] args){
    Veiculo ferrari = new Veiculo();
    Veiculo onibus = new Veiculo();
    Ferrari ferrariConvertida = (Ferrari) ferrari;
    Onibus onibusConvertido = (Onibus) onibus;
    System.out.println("Uma ferrari tem espaço para " + ferrariConvertida.informaLotacao() + " e possui " + ferrariConvertida.informaPotencia());
    System.out.println("Um ônibus tem espaco para " + onibusConvertido.informaLotacao() + " e funciona com " + onibusConvertido.informaCombustivel());
  }
}

Bom neste caso, você prova que o compilador só olha para a árvore de herança, porque ele não vai reclamar de nada. Entretanto, ao executar o código você vai receber um java.lang.ClassCastException, porque as variáveis de referência ferrari e onibus agora são do tipo Veiculo e um tipo generalizado não cabe em um tipo especializado, ou seja, Ferrari é um Veiculo, mas Veiculo, não necessariamente, é uma Ferrari. Ou ainda, Veiculo pode ser um Onibus, que é bem diferente de uma Ferrari, concorda?

Saber o que não compila e o que compila e não roda é fundamental para o exame.

Forte abraço e até a próxima.

Mauricio da Silva Marinho.

[Java] Overloading ou Overriding? (Parte 2/2)

Uns chamam de sobrescrita outros de subscrita, eu acho subscrita feio, mas sobrescrita está errado, já que overriding não quer dizer isto. Bem vou adotar subscrita e os que tiverem outras idéias podem postar como resposta a este post. Ficarei muito grato!

Overriding (Subscrita).

Subscrita é dar outra implementação a um método que já existe na classe herdada, exceto, é claro, se o método em questão for declarado, na classe mãe, como final ou static.

É o que ocorre com os métodos abstratos herdados de uma superclasse abstrata. Neste caso, a menos que a subclasse também seja abstrata, é obrigatório prover a implementação do método.

Então podemos dizer sem medo de errar, que a subclasse concreta subscreve os métodos abstratos herdados de uma superclasse abstrata.

Nestes casos, você concorda que poderemos ter várias implementações para cada subclasse que subscrever um método abstrato herdado de uma mesma subclasse?

Então podemos dizer que teremos, neste caso, várias formas (implementações) do mesmo método?

É claro que sim, concordo com você, então estamos diante de um polimorfismo, correto?

Correto, você acertou novamente.

Veja um exemplo para prosseguirmos em nosso raciocínio:

public class Animal{
  public void moveSe(){
    System.out.println("Sai de um lugar para outro.");
  }
}

public class Cobra extends Animal{
  public void moveSe(){
    System.out.println("Sai de um lugar para outro, rastejando.");
  }
}

public class Canguru extends Animal{
  public void moveSe(){
    System.out.println("Sai de um lugar para outro, saltando.");
  }
}

public class TesteAnimal(){
  public static void main(String[] args){
    Animal cobra = new Cobra();
    Animal canguru = new Canguru();
    cobra.moveSe();
    canguru.moveSe();
  }
}

O exemplo anterior, compilando sem problemas, produzirá que saída ao executarmos a classe TestAnimal?

Exatamente, a saída será:

Sai de um lugar para o outro, rastejando.
Sai de um lugar para o outro, saltando.

Isto porque, apesar de cobra e canguru serem do tipo Animal, cada uma destas referências, aponta para uma instância de Cobra e Canguru, respectivamente. Portanto, o método subscrito será invocado na execução.

Lembre-se que isto só é possível porque Animal também tem um método moveSe(). Caso contrário, o método moveSe() não teria visibilidade para uma referência do tipo Animal. E mesmo porque, se Animal não tivesse moveSe(), não estaríamos falando de subscrita (e o animal não estaria no código).

Veja outras restrições para um método subscrito:
  • Não pode ter um modificador de acesso mais restritivo.
  • Não pode ter uma assinatura diferente.
  • Não pode ter um tipo de retorno diferente.
  • Não pode ter nível de acesso mais restritivo.
  • Apenas métodos herdados podem ser subscritos. Lembrando, se o método original for final ou static, não poderá ser subscrito, e se for private, só poderá ser subscrito por subclasses no mesmo pacote da superclasse.
  • Não pode lançar exceções verificadas novas ou mais abrangentes que as declaradas pelo método original da superclasse.


É isso! Espero ter-lhe ajudado de alguma forma com estas informações. Qualquer dúvida ou contribuição, fique a vontade para postar. Terei imenso prazer em responder todas.

Mauricio da Silva Marinho.


[Java] Overloading ou Overriding? (Parte 1/2)

Qual a diferença entre estas duas técnicas de programação OO? O que significam?

Overloading (Sobrecarga).

Um método sobrecarregado é aquele que aparece mais de uma vez em uma classe (ou subclasse) com assinatura (lista de argumentos) distinta.

O tipo de retorno e a implementação, podem ser, opcionalmente diferentes. Então podemos dizer que métodos sobrecarregados:

  • devem possuir assinaturas distintas.
  • podem possuir tipos de retorno distintos.
  • podem possuir modificadores de acesso distintos.
  • podem declarar exceções verificadas novas ou mais abrangentes.

Veja o seguinte exemplo de método sobrecarregado:

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

//Chamando o metodo soma sobrecarregado.
public class TestaAritmetica{
  public static void main(String[] args){
    Aritmetica calculadora = new Aritmetica();
    int a = 10;
    int b = 20;
    int somaDosInts = calculadora.soma(a, b);
    int somaDosDoubles = calculadora.soma(10.5 + 20.5);
  }
}

No exemplo anterior, na classe TestaAritmetica, somaDosInts recebe o retorno da chamada a primeira forma do método sobrecarregado Aritmetica.soma que é soma(int, int). E somaDosDoubles recebe o retorno da chamada a segunda forma do método sobrecarregado Aritmetica.soma, que é soma(double, double). É um método polimórfico, pois tem mais de uma forma.

É o tipo de argumento que irá acionar a forma chamada de um método sobrecarregado, pois a identidade do método é dada pela sua assinatura. Veja este outro exemplo:

class Pet{}

class Cao extends Pet{}

class Lar{
  public void maisUmNaFamilia(Pet pet){
    System.out.println("Chegou um pet na casa!");
  }
  public void maisUmNaFamilia(Cao cao){
    System.out.println("Chegou um cão na casa!");
  }
  public static void main(String[] args){
    Lar lar = new Lar();
    Pet pet = new pet();
    Cao cao = new Cao();
    Pet refParaCao = new Cao();
    lar.maisUmNaFamilia(pet);
    lar.maisUmNaFamilia(cao);
    lar.maisUmNaFamilia(refParaCao);
  }
}

E no caso do exemplo anterior, qual seria a saída?

Lembre-se: a forma do método sobrecarregado será identificada pelos tipos da lista de parâmetros, pela assinatura do método. Logo, independente de refParaCao guardar uma referência de Cao, seu tipo é Pet e por isso o método forma do método sobregarregado Lar.maisUmNaFamilia que será invocada é: Lar.maisUmNaFamilia(Pet). Potanto a saída será:

Chegou um pet na casa!
Chegou um cao na casa!
Chegou um pet na casa!

É isso aí! Estes conhecimentos são cobrados na prova SCJP/OCJP e espero que lhe ajudem a esclarecer algumas questões importantes. No próximo post discutiremos Overriding, as confusões entre os dois conceitos e como o examinador pode se aproveitar disso para lhe confundir no exame.

Forte abraço,

Mauricio da Silva Marinho