Convenções de nomenclatura, por exemplo, variáveis ​​locais e de parâmetro [fechadas]

13

Eu estava discutindo com as convenções de codificação de um desenvolvedor sênior para aplicar em nossos projetos (principalmente projetos Java / JEE). Eu discordei de uma convenção que ele propôs:

Os nomes de variáveis ​​de instância devem começar com "_", variáveis ​​locais com "loc" e parâmetros de método com "par", para que seja fácil identificar a origem e o escopo da variável.

Embora ele tenha apresentado argumentos a favor da memória e da legibilidade a curto prazo, discordei do fato de que ela diminui bastante a legibilidade, os IDEs gostam de variáveis ​​de formato Eclipse de maneira diferente, dependendo do tipo, e esse problema seria evitado com um bom design de classe e método.

Você tem alguma opinião, argumento ou estudo que apóie meu argumento (ou se oponha a ele)?

HH
fonte
Você diz que discordou do "fato de diminuir a legibilidade". Não estou dizendo que você está errado, mas que evidência você forneceu para apoiar essa alegação? Eu não estou ciente de qualquer pesquisa que diz que vai diminuir a legibilidade (eu estudei cognitiva na psicologia pós-graduação antes de se tornar um desenvolvedor, por isso esta é uma área de interesse para mim.)
AdamJonR
Eu quis dizer isso, pois é desordenado. Mas não tenho outra evidência além da minha opinião pessoal
HH
Os prefixos duplicam as informações que já estão contidas no código e exibidas em qualquer ambiente meio decente. E como todos sabemos, informações duplicadas podem se tornar inconsistentes. DRY deve indicar que você não deve usar os prefixos.
Julia Hayward

Respostas:

15

Como a Wikipedia diz sobre o assunto - Regras para nomear java,

Variáveis ​​locais, variáveis ​​de instância e variáveis ​​de classe também são gravadas em lowerCamelCase. Os nomes de variáveis ​​não devem começar com caracteres de sublinhado (_) ou cifrão ($), mesmo que ambos sejam permitidos. Certas convenções de codificação afirmam que sublinhados devem ser usados ​​para prefixar todas as variáveis ​​de instância, para melhorar a leitura e o entendimento do programa.

De acordo com minha experiência com os padrões de codificação, os nomes de variáveis ​​de instância começam com "_" não são muito bons, como dizem os padrões da wikipedia.

variáveis ​​locais com "loc" e parâmetros de método com "par", como você disse que seria fácil identificar uma origem e escopo de variáveis, mas deve ser para você, não para os outros programadores que podem passar algum dia pelo seu código de manutenção .

De acordo com a especificação do Código Limpo sobre os métodos, estes devem ser tão curtos quanto possível para facilitar a leitura e os nomes de variáveis ​​não devem ser mapeados, eles devem ser relevantes para sua operação que seu método executa.

Prefixos de membro / escopo, você também não precisa mais prefixar variáveis ​​de membro com m_. Suas classes e funções devem ser pequenas o suficiente para que você não precise delas. E você deve usar um ambiente de edição que destaque ou colore os membros para torná-los distintos.

public class Part {
private String m_dsc; // The textual description
void setName(String name) {
m_dsc = name;
}
}

public class Part {
String description;
void setDescription(String description) {
this.description = description;
}
}

Além disso, as pessoas aprendem rapidamente a ignorar o prefixo (ou sufixo) para ver a parte significativa do nome. Quanto mais lemos o código, menos vemos os prefixos. Eventualmente, os prefixos se tornam uma confusão invisível e um marcador de código mais antigo.

Niranjan Singh
fonte
4

Esta é uma pergunta antiga, mas eu vou postar aqui de qualquer maneira. Tenho mais de 20 anos de programação e lidar com o código de outras pessoas.

Eu acho que nomear sua variável com uma indicação curta quanto ao escopo dela é realmente realmente útil para a próxima pessoa (ou você) que analisará seu código.

Ainda não se olha o código em um IDE com cores bonitas (e não me lembro o que as cores significam e o IDE diferente mostra cores diferentes, etc.).

É verdade que os métodos devem ser curtos o suficiente para que não sejam carregados com toneladas de variáveis ​​e toneladas de código, mas mesmo com uma curta - quando você olha para um código totalmente desconhecido, às vezes é difícil dizer se uma variável é uma variável de classe, local variável ou parâmetro do método.

Ser capaz de distinguir rapidamente torna muito fácil revisar o código com o qual você não está familiarizado.

Veja este exemplo:

public <T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz) {
    int startRecord = 0;
    ElasticsearchPersistentEntity persistentEntity = getPersistentEntityFor(clazz);
    String indexName = isNotBlank(query.getIndexName()) ? query.getIndexName() : persistentEntity.getIndexName();
    String type = isNotBlank(query.getType()) ? query.getType() : persistentEntity.getIndexType();

    Assert.notNull(indexName, "No 'indexName' defined for MoreLikeThisQuery");
    Assert.notNull(type, "No 'type' defined for MoreLikeThisQuery");
    Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery");

    MoreLikeThisRequestBuilder requestBuilder = client.prepareMoreLikeThis(indexName, type, query.getId());

    if (query.getPageable() != null) {
        startRecord = query.getPageable().getPageNumber() * query.getPageable().getPageSize();
        requestBuilder.setSearchSize(query.getPageable().getPageSize());
    }
    requestBuilder.setSearchFrom(startRecord);

    if (isNotEmpty(query.getSearchIndices())) {
        requestBuilder.setSearchIndices(toArray(query.getSearchIndices()));
    }
    if (isNotEmpty(query.getSearchTypes())) {
        requestBuilder.setSearchTypes(toArray(query.getSearchTypes()));
    }
    if (isNotEmpty(query.getFields())) {
        requestBuilder.setField(toArray(query.getFields()));
    }
    if (isNotBlank(query.getRouting())) {
        requestBuilder.setRouting(query.getRouting());
    }
    if (query.getPercentTermsToMatch() != null) {
        requestBuilder.setPercentTermsToMatch(query.getPercentTermsToMatch());
    }
    if (query.getMinTermFreq() != null) {
        requestBuilder.setMinTermFreq(query.getMinTermFreq());
    }
    if (query.getMaxQueryTerms() != null) {
        requestBuilder.maxQueryTerms(query.getMaxQueryTerms());
    }
    if (isNotEmpty(query.getStopWords())) {
        requestBuilder.setStopWords(toArray(query.getStopWords()));
    }
    if (query.getMinDocFreq() != null) {
        requestBuilder.setMinDocFreq(query.getMinDocFreq());
    }
    if (query.getMaxDocFreq() != null) {
        requestBuilder.setMaxDocFreq(query.getMaxDocFreq());
    }
    if (query.getMinWordLen() != null) {
        requestBuilder.setMinWordLen(query.getMinWordLen());
    }
    if (query.getMaxWordLen() != null) {
        requestBuilder.setMaxWordLen(query.getMaxWordLen());
    }
    if (query.getBoostTerms() != null) {
        requestBuilder.setBoostTerms(query.getBoostTerms());
    }

    SearchResponse response = requestBuilder.execute().actionGet();
    return resultsMapper.mapResults(response, clazz, query.getPageable());
}

Agora, avalie o tempo e observe o código (extraído de ElasticsearchTemplate do projeto spring-data-elasticsearch - o código que eu estava revisando que me levou a pesquisar no Google o que as pessoas dizem sobre convenções de nomenclatura).

  • Qual é o código resultsMapper?
  • É requestBuildingum parâmetro?
  • etc ...

Aqui está minha sugestão simples sobre como as variáveis ​​devem ser nomeadas:

  • Atributos estáticos da classe (por exemplo, constantes): ALL_CAPS_WITH_UNDERSCORES (por exemplo HOST_NAME).
  • Atributos de classe (variáveis ​​de instância de classe): camelCase (por exemplo resultsMapper).
  • Parâmetros do método: prefixados com a(por exemplo aQuery, aClazz).
  • Variáveis ​​locais: prefixadas com my(por exemplo myIndexName, myType).

O código acima se torna:

public <T> Page<T> moreLikeThis(MoreLikeThisQuery aQuery, Class<T> aClazz) {
  int myStartRecord = 0;
  ElasticsearchPersistentEntity myPersistentEntity = getPersistentEntityFor(aClazz);
  String myIndexName = isNotBlank(aQuery.getIndexName()) ? aQuery.getIndexName() : myPersistentEntity.getIndexName();
  String myType = isNotBlank(aQuery.getType()) ? aQuery.getType() : myPersistentEntity.getIndexType();

  Assert.notNull(myIndexName, "No 'indexName' defined for MoreLikeThisQuery");
  Assert.notNull(myType, "No 'type' defined for MoreLikeThisQuery");
  Assert.notNull(aQuery.getId(), "No document id defined for MoreLikeThisQuery");

  MoreLikeThisRequestBuilder myRequestBuilder = client.prepareMoreLikeThis(myIndexName, myType, aQuery.getId());

  if (aQuery.getPageable() != null) {
     myStartRecord = aQuery.getPageable().getPageNumber() * aQuery.getPageable().getPageSize();
     myRequestBuilder.setSearchSize(aQuery.getPageable().getPageSize());
  }
  myRequestBuilder.setSearchFrom(myStartRecord);

  if (isNotEmpty(aQuery.getSearchIndices())) {
     myRequestBuilder.setSearchIndices(toArray(aQuery.getSearchIndices()));
  }
  if (isNotEmpty(aQuery.getSearchTypes())) {
     myRequestBuilder.setSearchTypes(toArray(aQuery.getSearchTypes()));
  }
  if (isNotEmpty(aQuery.getFields())) {
     myRequestBuilder.setField(toArray(aQuery.getFields()));
  }
  if (isNotBlank(aQuery.getRouting())) {
     myRequestBuilder.setRouting(aQuery.getRouting());
  }
  if (aQuery.getPercentTermsToMatch() != null) {
     myRequestBuilder.setPercentTermsToMatch(aQuery.getPercentTermsToMatch());
  }
  if (aQuery.getMinTermFreq() != null) {
     myRequestBuilder.setMinTermFreq(aQuery.getMinTermFreq());
  }
  if (aQuery.getMaxQueryTerms() != null) {
     myRequestBuilder.maxQueryTerms(aQuery.getMaxQueryTerms());
  }
  if (isNotEmpty(aQuery.getStopWords())) {
     myRequestBuilder.setStopWords(toArray(aQuery.getStopWords()));
  }
  if (aQuery.getMinDocFreq() != null) {
     myRequestBuilder.setMinDocFreq(aQuery.getMinDocFreq());
  }
  if (aQuery.getMaxDocFreq() != null) {
     myRequestBuilder.setMaxDocFreq(aQuery.getMaxDocFreq());
  }
  if (aQuery.getMinWordLen() != null) {
     myRequestBuilder.setMinWordLen(aQuery.getMinWordLen());
  }
  if (aQuery.getMaxWordLen() != null) {
     myRequestBuilder.setMaxWordLen(aQuery.getMaxWordLen());
  }
  if (aQuery.getBoostTerms() != null) {
     myRequestBuilder.setBoostTerms(aQuery.getBoostTerms());
  }

  SearchResponse myResponse = myRequestBuilder.execute().actionGet();
  return resultsMapper.mapResults(myResponse, aClazz, aQuery.getPageable());

}

Isso é perfeito? Acho que não. Mas o acima, no que diz respeito às variáveis, agora é mais fácil de ler. Existem outras coisas, como alinhamento e espaçamento, que não abordarei nesta resposta, pois não está relacionado à pergunta, o que facilitaria a leitura também.

Você não gosta de Camel Case? Ótimo, use sublinhados, etc., mas prefixe suas variáveis ​​locais e seus parâmetros para torná-los diferentes das variáveis ​​de instância de classe.

Você não gosta ae my- tudo bem, apenas seja consistente no seu projeto e use outra coisa ... mas use alguma coisa.

Regra # 1: consistência dentro do projeto.

Regra nº 2: facilite a leitura e não exija que o leitor saiba tudo antes de poder aprender.

ETL
fonte
3

Isso é em grande parte uma questão de preferência e, como tal, não há resposta 'correta'. Portanto, essa pergunta pode realmente ser encerrada. Mas, antes disso, deixe-me dizer que concordo totalmente com você. Prefixos diminuem a visibilidade no que me diz respeito. Muito menos o fato de que, se houver prefixos, eles devem ser usados ​​para coisas mais úteis, como a intenção original da Notação Húngara , e não para coisas que seu IDE possa destacar de qualquer maneira.

Eu uso o SentençaCase para dados de instância (sejam variáveis ​​ou constantes) e lower_case para parâmetros e variáveis ​​locais, já que realmente há muito pouca ou nenhuma diferença entre os dois. Eu nunca, jamais, uso headlessCamelCase porque é coxo : um identificador de componente único parece minúsculo, mesmo se ele pretendesse ser headlessCamelCase.

Mike Nakis
fonte