Este artigo lista alguns erros típicos que vejo no código Java dos meus colegas ao meu redor. Obviamente, a análise estática de código (nossa equipe usa qulice) não será capaz de encontrar todos os problemas, por isso os listo aqui.
Se você acha que algo está faltando, por favor me avise e terei prazer em adicioná-lo.
Todos esses erros listados abaixo estão basicamente relacionados à programação orientada a objetos, especialmente OOP do Java.
Nome da classe
Leia este pequeno artigo "O que são objetos". Uma classe deve ser uma entidade abstrata na vida real, não “validadores”, “controladores” e “gerentes”. Se o nome da sua classe terminar com "er" - é um design ruim.
É claro que as classes de ferramentas também são antipadrões, como StringUtils, FileUtils e IOUtils do Apache. Todos os itens acima são exemplos de design ruim. Leitura adicional: Alternativas para classes de ferramentas em OOP.
É claro que não use prefixos ou sufixos para distinguir classes de interfaces. Por exemplo, estes nomes estão errados: IRecord, IfaceEmployee ou RecordInterface. De modo geral, o nome da interface deve ser o nome da entidade da vida real e o nome da classe deve descrever os detalhes de sua implementação. Se não houver nada de especial na implementação, você pode chamá-la de Padrão, Simples ou algo semelhante. Por exemplo:
Copie o código do código da seguinte forma:
a classe SimpleUser implementa o usuário {};
a classe DefaultRecord implementa Record {};
classe Sufixo implementa Nome {};
classe Validada implementa Conteúdo {};
nome do método
Os métodos podem retornar valores ou ser nulos. Se um método retornar um valor, seu nome deverá descrever o que ele retorna, por exemplo (nunca use o prefixo get):
Copie o código do código da seguinte forma:
booleano isValid(String nome);
String conteúdo();
int ageOf(Arquivo arquivo);
Se retornar nulo, seu nome deverá explicar o que faz. por exemplo:
Copie o código do código da seguinte forma:
void salvar (arquivo arquivo);
processo vazio(Trabalho);
void anexar (arquivo arquivo, linha String);
Há apenas uma exceção às regras mencionadas – o método de teste do JUnit não conta. Isso será discutido abaixo.
O nome do método de teste
Nos casos de teste JUnit, o nome do método deve ser uma instrução em inglês sem espaços. Ficará mais claro com um exemplo:
Copie o código do código da seguinte forma:
/**
* HttpRequest pode retornar seu conteúdo em Unicode.
* @throws Exceção se o teste falhar
*/
public void retornaItsContentInUnicode() lança exceção {
}
A primeira frase do seu JavaDoc deve começar com o nome da classe que você deseja testar, seguida por can. Portanto, sua primeira frase deve ser algo como “alguém pode fazer alguma coisa”.
O nome do método também é o mesmo, só que sem o tema. Se eu adicionar um assunto no meio do nome do método, recebo uma frase completa, como no exemplo acima: "HttpRequest retorna seu conteúdo em unicode".
Observe que o nome do método de teste não começa com can. Somente comentários no JavaDoc começarão com can. Além disso, os nomes dos métodos não devem começar com um verbo.
Na prática, é melhor declarar o método de teste para lançar uma exceção.
nome da variável
Evite combinar nomes de variáveis, como timeOfDay, firstItem ou httpRequest. Isto é verdade para variáveis de classe e variáveis dentro de métodos. Os nomes das variáveis devem ser longos o suficiente para evitar ambiguidade dentro do seu escopo visível, mas não muito longos, se possível. O nome deve ser um substantivo no singular ou plural, ou uma abreviatura apropriada. por exemplo:
Copie o código do código da seguinte forma:
Lista<String> nomes;
void sendThroughProxy (arquivo arquivo, protocolo protocolo);
conteúdo de arquivo privado;
solicitação HttpRequest pública;
Às vezes, se o construtor salvar os parâmetros de entrada em um objeto recém-inicializado, os nomes de seus parâmetros e atributos de classe poderão entrar em conflito. Nesse caso, minha sugestão é retirar as vogais e usar abreviações.
Exemplo:
Copie o código do código da seguinte forma:
mensagem de classe pública {
destinatário de string privado;
Mensagem pública (String rcpt) {
este.destinatário = rcpt;
}
}
Muitas vezes, você pode dizer qual nome a variável deve receber observando o nome de sua classe. Basta usar sua forma minúscula, que é confiável assim:
Copie o código do código da seguinte forma:
Arquivo de arquivo;
Usuário usuário;
Filial;
No entanto, você nunca deve fazer isso com tipos primitivos, como número inteiro ou string String.
Se houver múltiplas variáveis de naturezas diferentes, considere usar adjetivos. por exemplo:
Copie o código do código da seguinte forma:
Contato de string (String à esquerda, String à direita);
Construtor
Desconsiderando exceções, deve haver apenas um construtor usado para armazenar dados em variáveis de objeto. Outros construtores chamam esse construtor com parâmetros diferentes. Por exemplo:
Copie o código do código da seguinte forma:
classe pública Servidor {
endereço de string privado;
servidor público (String uri) {
este.endereço = uri;
}
servidor público (URI uri) {
this(uri.toString());
}
}
variável única
Variáveis descartáveis devem ser evitadas a todo custo. O que quero dizer com "única" aqui é uma variável que é usada apenas uma vez. Por exemplo, este:
Copie o código do código da seguinte forma:
String nome = "dados.txt";
retornar novo arquivo(nome);
As variáveis acima são usadas apenas uma vez, então este código pode ser reestruturado assim:
Copie o código do código da seguinte forma:
retornar novo arquivo("dados.txt");
Às vezes, em casos raros — principalmente para uma formatação mais bonita — variáveis descartáveis podem ser usadas. No entanto, isso deve ser evitado tanto quanto possível.
anormal
Escusado será dizer que você nunca deve engolir uma exceção, mas deve rejeitá-la o mais alto possível. Os métodos privados devem sempre lançar exceções verificadas.
Não use exceções para controle de fluxo. Por exemplo, o código a seguir está errado:
Copie o código do código da seguinte forma:
tamanho interno;
tentar {
tamanho = this.fileSize();
} catch (IOException ex) {
tamanho = 0;
}
Então, o que você deve fazer se IOException solicitar “Disco cheio”? Você ainda pensaria que o tamanho do arquivo é 0 e continuaria o processamento?
recuo
Em relação ao recuo, a regra principal é que o colchete de abertura termine no final da linha ou feche na mesma linha (o oposto vale para colchetes de fechamento). Por exemplo, o seguinte está incorreto porque o primeiro colchete de abertura não está fechado na mesma linha e há outros caracteres depois dele. O segundo colchete também é problemático porque é precedido por caracteres, mas o colchete de abertura correspondente não está na mesma linha:
Copie o código do código da seguinte forma:
arquivo final arquivo = novo arquivo (diretório,
"arquivo.txt");
O recuo correto deve ser assim:
Copie o código do código da seguinte forma:
StringUtils.join(
Arrays.asList(
"primeira linha",
"segunda linha",
StringUtils.join(
Arrays.asList("a", "b")
)
),
"separador"
);
A segunda regra importante sobre recuo é que você deve tentar escrever tantos caracteres quanto possível em uma linha ao mesmo tempo - o limite máximo é de 80 caracteres. O exemplo acima não satisfaz este ponto, também pode ser reduzido:
Copie o código do código da seguinte forma:
StringUtils.join(
Arrays.asList(
"primeira linha", "segunda linha",
StringUtils.join(Arrays.asList("a", "b"))
),
"separador"
);
constantes redundantes
Constantes de classe devem ser usadas quando você deseja compartilhar informações dentro dos métodos de uma classe. As informações devem ser exclusivas para sua classe. Não use constantes como substitutos de strings ou literais numéricos - esta é uma prática muito ruim e poluirá seu código. Constantes (como qualquer objeto em POO) devem ter seu próprio significado no mundo real. Vamos ver o que essas constantes significam na vida real:
Copie o código do código da seguinte forma:
classe Documento {
private static final String D_LETTER = "D"; // má prática
private static final String EXTENSION = ".doc";/boa prática
}
Outro erro comum é usar constantes em testes unitários para evitar strings redundantes ou literais numéricos em métodos de teste. Não faça isso! Cada método de teste deve ter seus próprios valores de entrada exclusivos.
Use novos textos ou valores em cada novo método de teste. Eles são independentes um do outro. Então, por que eles ainda compartilham as mesmas constantes de entrada?
Acoplamento de dados de teste
Aqui está um exemplo de acoplamento de dados em um método de teste:
Copie o código do código da seguinte forma:
Usuário usuário = novo Usuário("Jeff");
//talvez algum outro código aqui
MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff"));
Na última linha, "Jeff" é acoplado à mesma string literal da primeira linha. Se depois de alguns meses alguém quiser alterar o valor da terceira linha, terá que perder tempo descobrindo onde "Jeff" também é usado no mesmo método.
Para evitar isso, é melhor introduzir uma variável.