Erros irritantes cometidos por programadores Java, parte 2

Este é o segundo post sobre erros irritantes cometidos por programadores Java. No primeiro post, citei seis erros relacionados a comparações entre valores e objetos. Neste segundo post, irei falar sobre outros nove erros também muito comuns, mas que devem ser evitados a todo o custo.

A numeração dos erros começa do 7 porque está continuando a numeração anterior, que parou no 6. Se houver um terceiro post sobre erros irritantes cometidos por programadores Java (e tá na cara que irá ter, pois há outros que deixarei para posts futuros, já que este ficou muito grande), a numeração começará do 16, continuando a numeração deste post.

7) Usar um loop com um contador (variável crescente de um em um) para iterar pelos valores de um List, acessando-os pelo método get: Seria o famoso for(int c = 0; c < list.size(); c++) valor = list.get(c); apesar de haver variantes ligeiramente diferentes, como decrescendo o contador ou usando o loop while e uma variável a parte como contador. Essa maneira de iterar pelos valores funciona bem para arrays, mas pode causar perda de performance em Lists, ou até comportamentos inesperados na lógica da iteração, caso objetos sejam removidos do List. Somente itere por um List dessa forma caso o mesmo seja com certeza um List baseado em array, como o ArrayList ou o Vector, e que não haja jeito do List ser baseado em outra implementação, como o LinkedList, que é baseado em lista encadeada, não em array, o que causaria perda de performance pelo fato de todos os objetos, desde o começo (ou o fim, o que estiver mais próximo) do List até a posição acessada, serem acessados, já que não há como acessar um objeto diretamente, a não ser que o objeto seja o primeiro ou o último da lista. E nunca remova valores do List, pois os valores seguintes serão recuados em uma posição, fazendo a iteração “comer” um valor a cada remoção de objeto do List. A não ser que o contador seja decrementado a cada remoção de objeto.

A maneira correta de se iterar por um List, bem como qualquer outra coleção do Java Collections Framework, é usando o loop for incrementado, algo como for(String s : list) valor = s; ou usando um Iterator, que pode ser obtido invocando o método iterator das coleções do Java. Na Internet, há vários exemplos ensinando a usar o Iterator e seus métodos, não é nada complicado. O Iterator possui a vantagem de permitir a exclusão de objetos usando o método remove.

8) Remover objetos de uma coleção sem ser pelo método remove da classe Iterator: Caso remova um objeto da coleção sem ser pelo Iterator, no caso de loops idiotas que citei no erro 7, a iteração pode “comer” um valor (a não ser que o contador seja decrementado) e, caso ocorra na maneira correta de iterar por uma coleção, será lançada uma ConcurrentModificationException, pois o Java não terá como saber qual a sequencia correta da iteração.

9) Suprimir o tratamento de uma Exception verificada: Se uma determinada Exception é verificada (isto é, que não extende a classe RuntimeException e, por isso, necessita que a mesma seja tratada num bloco try-catch), logo ela necessita que ela seja tratada de alguma forma. Ela é verificada por algum motivo, se ela não precisasse ser tratada, ela seria uma Exception não verificada.

Suprimir o tratamento de uma Exception verificada pode fazer com que um erro que ocorra no programa passe despercebido, ferrando com a lógica do bagulho. Se uma Exception é verificada, deve ser tratada, nem que seja para mostrar uma mensagem de erro.

10) Usar objetos sincronizados desnecessariamente: Objetos sincronizados seriam os pertencentes a classes sincronizadas, isto é, que foram implementadas de modo que os objetos delas podem ser acessados por threads simultânas sem ocorrer inconsistência nos valores dos mesmos. Exemplos de classes sincronizadas são a Vector, a Hashtable e a StringBuffer. Acontece que essas classes possuem performance inferior em relação às suas contrapartes não sincronizadas (ArrayList, HashMap e StringBuilder, em relação às três que citei). Somente se houver acesso concorrente que tais classes devem ser usadas.

11) Concatenar Strings com sinal de adição: A cada vez que uma String é concatenada com outra, mesmo se existir mais de uma concatenação em uma mesma linha, uma nova String será criada, poluindo o pool de Strings com Strings desnecessárias. Vale lembrar que, uma vez que uma String é criada no pool, ela só sairá de lá quando o programa for encerrado. Use a classe StringBuilder para concatenar Strings, passando-as como parâmetro do método append e depois pegando o resultado da concatenação pelo método toString. Isto evita lixo no pool de Strings e faz a concatenação ter performance melhor.

Pior que simplesmente concatenar Strings com sinal de adição, é passar duas ou mais Strings sendo concatenadas com sinal de adição como parâmetro no método append da classe StringBuilder. Se já está usando StringBuilder, por que vai concatenar com sinal de adição, se poderia passar cada uma das Strings separadamente pelo o método append?

12) Programar de forma procedural: Java é uma linguagem orientada a objetos, não é uma linguagem procedural, onde o código descreve um procedimento do início ao fim, num bloco enorme de código. Mas ainda assim, há programadores que não separam os códigos responsáveis por cada tarefa em métodos distintos e botam tudo num método só, programando de forma procedural no Java e não aproveitando a orientação a objetos, que tem como principal finalidade (ou uma das principais finalidades) o reaproveitamento do código. Colocar tudo num método só acaba prejudicando a manutenebilidade do código, fazendo-o ser um código de manutenção mais difícil, além de prejudicar seu posterior reaproveitamento.

13) Repetir o mesmo código várias vezes em um programa: Isto prejudica a manutenebilidade e a possibilidade de reaproveitamento do código. Se isto estiver acontecendo, é melhor escrever um método com o código e chamar este método em todos lugares onde o código repetido estava. São para isto que os métodos servem.

14) Não separar o código do software e misturar código de telas gráficas com código de acesso a banco de dados e com código de outras regras de negócio: Isto prejudica bastante a manutenebilidade e a possibilidade de reaproveitamento do código, ao ponto deste, em muitos casos, virar um lixo. Aí, quando precisa dar manutenção no programa, é aquela tristeza. Mexe numa coisa e estraga outra, pois uma coisa tá colada na outra. Vai tentar aproveitar um código em outro lugar e não dá, pois o mesmo tá acoplado com outra parte que não tem nada a ver.

O código de um software deve ser sempre separado em pelo menos três camadas: a de negócios, que contém a regra de negócios do programa; a de persistência, que contém o código relativo a acessos ao banco de dados; e a de apresentação, que contém o código das telas gráficas (ou o código relativo à geração do HTML, caso este seja uma aplicação web). O software pode ser separado em apenas duas camadas, caso este não possua acesso a banco de dados (por exemplo, uma calculadora). E somente se o software for muito pequeno que o código pode ficar tudo junto (por exemplo, um programa que calcula o índice de massa corporal, que faz apenas isso, um único cálculo, seria só uma tela pequena com dois campos para informar peso e altura e outro para exibir o IMC e um cálculo a ser feito).

15) Usar classes e métodos depreciados ou obsoletos: Se estão marcados como depreciados (isto é, com a anotação @Deprecated), significa que seu uso é desencorajado e que há alternativas melhores de se chegar a mesmo resultado, com outros métodos e classes. Os métodos depreciados só continuam existindo por questões de retrocompatibilidade com aplicações antigas, da época que estes métodos não estavam depreciados, e não devem ser usados em novos códigos.

Classes nas quais não foram marcadas como depreciadas mas que sabe-se que são obsoletas (como as classes StringTokenizer e Stack, onde consta no próprio Javadoc das classes que as mesmas são obsoletas) devem ter seu uso evitado, pois há alternativas melhores a elas. A StringTokenizer, por exemplo, ficou obsoleta após passas a existir o método split da classe String, que é mais simples. Já a Stack foi substituída pelas classes que implementam a interface Deque, que possui métodos equivalentes aos da Stack e possuem performance melhor por não serem sincronizadas.

Bom, por hoje é só, mas futuramente, irei escrever pelo menos mais outro post acerca de erros cometidos por programadores Java, que será a continuação deste aqui, assim como este post foi a continuação do outro post, que foi a parte 1.

Anúncios

Uma resposta to “Erros irritantes cometidos por programadores Java, parte 2”

  1. Nathalie Says:

    Dicas que devem ser seguidas, mas que insistimos em perpetuar. Legal seu post. Onde está o primeiro ?

Comente este post!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s