Um breve resumo das práticas recomendadas de codificação Java

com base nos padrões de codificação da Oracle, Google, Twitter e Spring Framework

O objetivo deste artigo é fornecer um resumo rápido do que fazer e não fazer, ou seja, preferir e evitar com base nos padrões de codificação de gigantes da tecnologia como Oracle, Google, Twitter e Spring Framework.

Você pode ou não concordar com algumas das práticas recomendadas apresentadas aqui, e isso é absolutamente bom, desde que exista algum padrão de codificação.

Por que codificar padrões em primeiro lugar? Existem muitas boas razões para você pesquisar no Google e eu vou deixar você com a ilustração a seguir

O documento de normas de codificação pode ser demorado e chato. Este artigo escolhe pedaços de convenções de codificação do Google, Oracle, Twitter e Spring e seu objetivo é fornecer a você um conjunto de práticas fáceis de seguir e menos chato para tornar seu código fácil de ler e manter.

Quase sempre você se unirá às equipes que trabalham no software existente e há uma boa chance de a maioria dos autores ter saído ou ter mudado para projetos diferentes, deixando-o preso a partes do código que o fazem questionar a humanidade.

Vamos mergulhar nas melhores práticas de vários padrões de codificação.

Arquivo de origem Java

A seguir, são consideradas práticas recomendadas quando se trata de arquivos de origem java:

  • O tamanho do arquivo de origem é inferior a 2.000 linhas de código
  • O arquivo de origem é organizado com comentário da documentação, declaração do pacote, seguido por um comentário da classe, importações agrupadas (última estática), assinatura de classe / interface e assim por diante, como mostrado abaixo
pacote com.example.model;
/ **
 * Perspectiva livre de implementação para ser lida pelos desenvolvedores
 * que pode não ter necessariamente o código fonte em mãos
 *
 * @ autor x, y, z
 * @encontro
 * @version
 * @direito autoral
 *
 * /
import com.example.util.FileUtil;
/ *
 * Comentário específico da classe opcional
 *
 * /
classe pública SomeClass {
  // Variáveis ​​estáticas em ordem de visibilidade
  estático público final Número inteiro PUBLIC_COUNT = 1;
  número inteiro final estático PROTECTED_COUNT = 1;
  final estático privado Número inteiro PRIVATE_COUNT = 1;
  // Variáveis ​​de instância em ordem de visibilidade
  nome da cadeia pública;
  String postalCode;
  endereço String privado;
  // Construtor e sobrecarregado em ordem sequencial
  public SomeClass () {}
  public SomeClass (String name) {
    this.name = nome;
  }
  // Métodos
  public String doSomethingUseful () {
    retornar "Algo útil";
  }
  // getters, setters, iguais, hashCode e toString no final
}

Nomeação

Os nomes de classe e interface são CamelCase e é recomendável usar a palavra inteira e evitar acrônimos / abreviações. Por exemplo, classe Raster ou classe ImageSprite

  • Pacote - nomes com.deepspace sobre com.deepSpace ou com.deep_space
  • Os nomes de arquivo são CamelCase e terminam com .java que corresponde ao nome da classe. Há uma classe pública por arquivo com cada classe de nível superior em seu arquivo
  • Método - os nomes devem ser verbos em maiúsculas e minúsculas com cada palavra interna em maiúscula, por exemplo, run (); ou runFast ();
  • Constantes - devem estar em maiúsculas com "_" separando cada palavra, por exemplo, int MIN_WIDTH = 44; e int MAX_WIDTH = 99;
  • Variável - um nome que informa ao leitor do programa o que a variável representa, ou seja, se você estiver armazenando uma nota de teste, escolha nota vs var1. Mantenha os nomes das variáveis ​​curtos, evitando incluir metadados.
// Prefer () - nomes de variáveis ​​curtos e descrevem o que ele armazena
int schoolId;
int [] filtrouSchoolIds;
int [] uniqueSchooldIds;
Mapear  usersById;
Valor da string;
// Evitar (x) - nomeação variável muito detalhada
int schoolIdentificationNumber;
int [] userProvidedSchoolIds;
int [] schoolIdsAfterRemovingDuplicates;
Mapear  idToUserMap;
String valueString;

Lembre-se - o nome da variável deve ser curto e informar facilmente ao leitor qual o valor que ela representa. Use seu julgamento.

Preferir e evitar

Formatação e recuo têm tudo a ver com organizar seu código para facilitar a leitura, incluindo espaçamento, comprimento da linha, quebra e quebra e assim por diante

  • Recuo - use 2 ou 4 espaços e mantenha a consistência
  • Comprimento da linha - Até 70 a 120 caracteres, dependendo do efeito da legibilidade. É importante eliminar a necessidade de rolagem horizontal e colocar quebras de linha após uma vírgula e um operador.

Métodos - Aqui está uma lista de práticas recomendadas

// Preferir () As quebras de linha são arbitrárias e são interrompidas após uma vírgula.
String downloadAnInternet (Internet na Internet, Tubos,
    Blogs da blogosfera, quantidade de largura de banda ) {
  tubes.download (internet);
}
// Evita (x) método difícil de diferenciar args para o corpo do método
String downloadAnInternet (Internet na Internet, Tubos,
    Blogs da blogosfera, quantidade de largura de banda ) {
    tubes.download (internet);
}
// Preferir () Adicione 8 (o dobro de 2 ou 4) espaços para o recuo profundo
horkingLongMethodName estático sincronizado privado (int anArg,
        Objeto anotherArg, String yetAnotherArg,
        Object andStillAnother) {
  ...
}
// Preferir () Fácil digitalização e espaço extra na coluna.
public String downloadAnInternet (
    Internet internet,
    Tubos tubos,
    Blogs da blogosfera,
    Quantidade de largura de banda ) {
  tubes.download (internet);
  ...
}
Um teste de unidade detectaria que

If-checks - O IMO que escreve código bem formatado facilita a identificação de erros de digitação e erros ao autor e aos revisores de código, veja abaixo:

// Evite (x) não omita {}
se (condição)
  declaração;
// Evitar (x)
se (x <0) negativo (x);
// Evitar (x)
if (a == b && c == d) {
...
}
// Preferir ()
if ((a == b) && (c == d)) {
...
}
// Preferir ()
se (condição) {
  afirmações;
} senão se (condição) {
  afirmações;
} senão se (condição) {
  afirmações;
}
// Evitar (x)
if ((condition1 && condition2)
    || (condição3 e& condição4)
    ||! (condition5 && condition6)) {// MAU ENVOLVIMENTO
    faça alguma coisa sobre isso(); // FAÇA ESTA LINHA FÁCIL DE PERDER
}
// Preferir ()
if ((condition1 && condition2)
        || (condição3 e& condição4)
        ||! (condition5 && condition6)) {
    faça alguma coisa sobre isso();
}

Operador ternário - E abaixo estão as práticas recomendadas

alpha = (aLongBooleanExpression)? beta: gama;
alpha = (aLongBooleanExpression)? beta
        : gama;
alpha = (aLongBooleanExpression)
        ? beta
        : gama;

Mudar - Quando se trata de mudar, é uma boa prática

  • Sempre tenha um caso padrão, mesmo sem código
  • Use / * cai em * / para indicar que o controle cai para o próximo caso
switch (condição) {
  caso ABC:
    afirmações;
  / * passa por * /
  caso DEF:
    afirmações;
    pausa;
  padrão:
    afirmações;
     pausa;
}

Mensagens de exceção - Ao lançar uma exceção, aqui estão exemplos de mensagens boas e pouco recuadas.

// Evitar (x) - não é fácil de ler
throw new IllegalStateException ("Falha ao processar a solicitação" + request.getId ()
    + "para usuário" + user.getId () + "query: '" + query.getText ()
    + "'");
// Preferir () - bastante mais fácil de ler
lançar nova IllegalStateException ("Falha ao processar"
    + "solicitação" + request.getId ()
    + "para usuário" + user.getId ()
    + "query: '" + query.getText () + "'");

Iteradores e fluxos - os fluxos estão se tornando mais comuns e, às vezes, podem ser muito complexos, portanto, é importante recuar para facilitar a leitura.

// Evitar (x) - não é fácil de ler
Iterável  módulos = ImmutableList.  construtor (). Add (new LifecycleModule ())
    .add (novo AppLauncherModule ()). addAll (application.getModules ()). build ();
// Preferir () - bastante mais fácil de ler
Módulos iteráveis ​​ = ImmutableList.  construtor ()
    .add (novo LifecycleModule ())
    .add (novo AppLauncherModule ())
    .addAll (application.getModules ())
    .Construir();
Basta seguir um padrão de codificação - qualquer

Declarações e atribuições - Uma declaração por linha é recomendada, pois incentiva os comentários, como mostrado abaixo.

// Preferir ()
nível int; // nível de indentação
int sizeMeter; // tamanho da tabela
// Evite (x) a favor do acima
int nível, sizeMeter;
// Preferir () - Incluir unidade no nome ou tipo da variável
long pollIntervalMs;
int fileSizeGb;
Quantidade  fileSize;
// Evitar (x) tipos de mistura
int foo, fooarray [];
// Evite (x) - não separe por vírgula
Format.print (System.out, “erro”), exit (1);
// Evitar (x) atribuição múltipla
fooBar.fChar = barFoo.lchar = 'c';
// Evite (x) atribuições incorporadas na tentativa de aumentar o desempenho // ou salve uma linha. Sou culpado de fazer isso :(
d = (a = b + c) + r;
// Prefere () acima
a = b + c;
d = a + r;
// Preferir ()
String [] args
// Evitar (x)
String args []
// Prefere () Use por muito tempo "L" em vez de "l" para evitar confusão com 1
tempo limite longo = 3000000000L;
// Evitar (x) - Difícil dizer a última letra é l e não 1
tempo limite longo = 3000000000l;

Coloque declarações apenas no início dos blocos (um bloco é um código cercado por chaves {e}). Não espere para declarar variáveis ​​até seu primeiro uso; pode confundir o programador incauto e dificultar a portabilidade do código dentro do escopo.

// Prefere () declara no início do bloco.
public void doSomething () {
  int whatIRepresent; // início do bloco do método
  se (condição) {
    int someFlag; // início do bloco "if"
    ...
  }
}

Também é importante evitar declarações locais que ocultam as declarações dos níveis mais altos e evitar confusões, como mostrado abaixo

int count;
...
public void doSomething () {
  se (condição) {
    int count; // EVITE!
    ...
  }
  ...
}

Espaçamento e quebras de linha - Evite a tentação de salvar 1 a 2 linhas de código em detrimento da legibilidade. Aqui estão todas as práticas recomendadas quando se trata de espaçamento e linhas em branco (um espaço em branco faz a diferença)

  • Uma (1) linha em branco entre os métodos e os desenvolvedores do Spring recomenda duas (2) linhas em branco após construtores, bloco estático, campos e classe interna
  • Operadores de bloco de espaço, ou seja, Use int foo = a + b + 1; sobre int foo = a + b + 1;
  • Separe todos os operadores binários, exceto "." Dos operandos, usando um espaço
  • A chave aberta “{” aparece no final da mesma linha da declaração ou método da declaração e a chave de fechamento “}” inicia uma linha recuada
// Preferir () - espaço após "while" e before "("
while (true) {
  ...
}
// Evitar (x) - diferente do espaço acima
while (true) {
  ...
}
// Preferir () - Não há espaço entre "doSomething" e "("
public void doSomething () {
  ...
}
// Evitar (x) - ao contrário do espaço acima
public void doSomething () {
  ...
}
// Preferir () - Adiciona um espaço após uma discussão
public void doSomething (int a, int b) {
  ...
}
// Preferir () - Espaço entre operando e operadores (ou seja, +, =)
a + = c + d;
a = (a + b) / (c * d);
while (d ++ = s ++) {
  n ++;
}

Documentação e Comentários

Vale ressaltar que quase todo o código muda ao longo de sua vida útil e haverá momentos em que você ou alguém está tentando descobrir o que um bloco complexo de código, método ou classe deve fazer, a menos que seja claramente descrito. A realidade é quase sempre como segue

Há momentos em que o comentário sobre um pedaço complexo de código, método, classe não agrega valor ou serve a seu propósito. Isso geralmente acontece quando se comenta.

Os comentários devem ser usados ​​para fornecer visões gerais do código e fornecer informações adicionais que não estão prontamente disponíveis no próprio código. Vamos começar. Existem dois tipos de comentários

Comentários de implementação - destinam-se a comentar o código ou comentar sobre uma implementação específica do código.

Comentários da documentação - destinam-se a descrever a especificação do código de uma perspectiva livre de implementação para ser lida por desenvolvedores que talvez não tenham necessariamente o código fonte em mãos.

A frequência dos comentários às vezes reflete a baixa qualidade do código. Quando você se sentir compelido a adicionar um comentário, considere reescrever o código para torná-lo mais claro.

Tipos de comentários de implementação

Existem quatro (4) tipos de comentários de implementação, como mostrado abaixo

  • Bloquear comentário - veja o exemplo abaixo
  • Comentário de linha única - quando o comentário não ultrapassa uma linha
  • Comentários finais - Comentário muito curto foi movido para a extremidade direita
  • Comentário de fim de linha - inicia um comentário que continua na nova linha. Pode comentar uma linha completa ou apenas uma linha parcial. Não deve ser usado em várias linhas consecutivas para comentários de texto; no entanto, ele pode ser usado em várias linhas consecutivas para comentar seções de código.
// Bloquear comentário
/ *
 * Uso: Fornece descrição de arquivos, métodos, estruturas de dados
 * e algoritmos. Pode ser usado no início de cada arquivo e
 * antes de cada método. Usado para comentários longos que não se encaixam em um
 * única linha. 1 Linha em branco para prosseguir após o comentário do bloco.
 * /
// Comentário de linha única
se (condição) {
 / * Manipule a condição. * /
  ...
}
// Comentário à direita
if (a == 2) {
 return TRUE; /* caso especial */
} outro {
 return isPrime (a); / * funciona apenas para a * / ímpar
}
// Comentário de fim de linha
if (foo> 1) {
  // Faça um flip duplo.
  ...
} outro {
  retorna falso; // Explique o porquê aqui.
}
// if (bar> 1) {
//
// // Faça um flip triplo.
// ...
//}
//outro
// retorna falso;

Comentários da documentação (por exemplo, Javadoc)

Javadoc é uma ferramenta que gera documentação HTML do seu código java usando os comentários que começam com / ** e terminam com * / - consulte a Wikipedia para obter mais detalhes sobre como o Javadoc funciona ou apenas para ler.

Aqui está um exemplo de Javadoc

/ **
 * Retorna um objeto de imagem que pode ser pintado na tela.
 * O argumento url deve especificar um {@link URL} absoluto. O nome
 * argumento é um especificador que é relativo ao argumento da URL.
 * 

 * Este método sempre retorna imediatamente, independentemente de o  * imagem existe. Quando este applet tenta desenhar a imagem no  * a tela, os dados serão carregados. As primitivas gráficas  * que desenham a imagem serão incrementados na tela.  *  * @param url, um URL absoluto, fornecendo a localização base da imagem  * @param nomeie o local da imagem, em relação ao argumento do URL  * @ retorne a imagem no URL especificado  * @ver imagem  * /  Imagem pública getImage (URL da URL, Nome da string) {         experimentar {             retornar getImage (novo URL (url, nome));         } captura (MalformedURLException e) {             return null;         }  }

E o acima resultaria em um HTML como segue quando o javadoc é executado no código que possui o acima

Veja aqui para mais

Aqui estão algumas tags principais que você pode usar para aprimorar a qualidade da documentação java gerada.

@author => @author Raf
@code => {@code A  C}
@deprecated => @deprecation-message
@exception => @exception IOException lançada quando
@link => {@link package.class # member label}
@param => @param descrição do nome do parâmetro
@return => O que o método retorna
@see => @see "string" OU @see  
@since => Para indicar a versão quando um método acessível ao público é adicionado

Para uma lista completa e uma descrição mais detalhada, consulte aqui

O padrão de codificação do Twitter desaconselha o uso da tag @author

O código pode mudar de mãos várias vezes durante a sua vida útil e, muitas vezes, o autor original de um arquivo de origem é irrelevante após várias iterações. Achamos que é melhor confiar no histórico de confirmação e nos arquivos OWNERS para determinar a propriedade de um corpo de código.

A seguir, são mostrados exemplos de como você pode escrever um comentário de documentação que seja esclarecedor, conforme descrito no padrão de codificação do Twitter

// Ruim.
// - O documento não diz nada que a declaração do método não o fez.
// - Este é o 'documento de preenchimento'. Seria aprovado nas verificações de estilo, mas
não ajuda ninguém.
/ **
 * Divide uma string.
 *
 * @param s Uma string.
 * @return Uma lista de strings.
 * /
Listar  divisão (String s);
// Melhor.
// - Sabemos no que o método se divide.
// - Ainda há um comportamento indefinido.
/ **
 * Divide uma string no espaço em branco.
 *
 * @param s A sequência a ser dividida. Uma sequência {@code null} é tratada como uma sequência vazia.
 * @return Uma lista das partes delimitadas por espaços em branco da entrada.
 * /
Listar  divisão (String s);
// Ótimo.
// - Cobre outro estojo de borda.
/ **
 * Divide uma string no espaço em branco. Caracteres de espaço em branco repetidos
 * estão recolhidos.
 *
 * @param s A sequência a ser dividida. Uma sequência {@code null} é tratada como uma sequência vazia.
 * @return Uma lista das partes delimitadas por espaços em branco da entrada.
 * /
Listar  divisão (String s);

É importante ser profissional quando se trata de escrever comentários

// Evitar (x)
// Eu odeio tanto xml / soap, por que não pode fazer isso por mim !?
experimentar {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}
// Preferir ()
// TODO (Jim): retire a validação do campo em uma biblioteca.
experimentar {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}

E é importante ter em mente para não documentar o método substituído, a menos que a implementação tenha sido alterada.

E aqui estão mais alguns pontos a serem lembrados

  • Evite importações de curingas - conforme descrito nos padrões de codificação do Twitter, torna a fonte da classe menos clara. Trabalho em equipe com uma mistura de usuários do Eclipse e do IntelliJ e descobri que o Eclipse remove as importações de caracteres curinga e o IntelliJ o apresenta. Provavelmente existe uma opção para desativá-lo, só queria apontar o padrão para os dois.
  • Sempre use a anotação @Override ao substituir
  • Incentive o uso de @Nullable quando um campo ou método retornar nulo
  • Use comentários especiais para trabalhos futuros e não se esqueça de deixar uma referência para si mesmo, para que outras pessoas saibam a quem fazer sua pergunta em vez de adivinhar, removê-la ou checar a culpa por descobrir quem a adicionou. Alguns IDEs como Eclipse e IntelliJ também ajudam a listá-los para facilitar o acesso, bem como um lembrete.
// FIXME (Raf): Uma mensagem acionável descreve o que precisa ser feito
// TODO (Raf): uma mensagem acionável descreve o que precisa ser feito

O jogo final é escrever um código que facilite a vida de futuros autores e mantenedores.

O jogo final

Outros materiais de leitura relevantes

Uma lista de artigos relevantes que são relevantes para escrever um código limpo, bem estruturado, fácil de ler e de manter. Se você quiser ler mais, recomende definitivamente o seguinte

e outra boa lista de dicas para escrever código limpo