Convenciones de nomenclatura, por ejemplo, variables locales y de parámetros [cerrado]

13

Estaba discutiendo con un desarrollador senior de convenciones de codificación para aplicar a nuestros proyectos (principalmente proyectos Java / JEE). No estoy de acuerdo con una convención que propuso:

Los nombres de las variables de instancia deben comenzar con "_", las variables locales con "loc" y los parámetros de método con "par", por lo que sería fácil identificar el origen y el alcance de la variable.

Si bien presentó argumentos a favor de la memoria a corto plazo y la legibilidad, no estuve de acuerdo con el hecho de que disminuye la legibilidad, los IDE como las variables de formato Eclipse de manera diferente dependiendo de su tipo, y este problema se evitaría con una buena clase y diseño de método.

¿Tiene alguna opinión, argumento o estudio que respalde mi punto (o se oponga)?

S.S
fuente
Usted dice que no está de acuerdo con "el hecho de que disminuye la legibilidad". No digo que esté equivocado, pero ¿qué evidencia proporcionó para respaldar esa afirmación? No conozco ninguna investigación que diga que disminuirá la legibilidad (estudié psicología cognitiva en la escuela de posgrado antes de convertirme en desarrollador, por lo que este es un área de interés para mí)
AdamJonR
Lo dije en serio, ya que está abarrotado. Pero no tengo más evidencia que mi opinión personal
HH
Los prefijos duplican la información que ya está contenida en el código y se muestra en cualquier entorno medio decente. Y como todos sabemos, la información duplicada puede volverse inconsistente. DRY debería indicarle que no use los prefijos.
Julia Hayward

Respuestas:

15

Como dice Wikipedia sobre el tema: Reglas para nombrar java,

Las variables locales, las variables de instancia y las variables de clase también se escriben en lowerCamelCase. Los nombres de las variables no deben comenzar con guiones bajos (_) o caracteres de signo de dólar ($), aunque ambos estén permitidos. Ciertas convenciones de codificación establecen que los guiones bajos deben usarse para prefijar todas las variables de instancia, para una mejor lectura y comprensión del programa.

Según mi experiencia con los estándares de codificación, los nombres de las variables de instancia comienzan con "_" no son muy buenos, como dicen los estándares de Wikipedia.

variables locales con "loc", y parámetros de método con "par", como dijiste, sería fácil identificar un origen y alcance de variable, pero debería ser para ti, no para los otros programadores que algún día pueden revisar tu código para mantenimiento .

Según la especificación Clean Code sobre los métodos, estos deben ser lo más cortos que pueda hacer para facilitar la lectura y los nombres de las variables no deben ser mapeados, deben ser relevantes para la operación que realiza su método.

Prefijos de miembro / alcance, ya no necesita prefijar las variables de miembro con m_. Sus clases y funciones deben ser lo suficientemente pequeñas como para que no las necesite. Y debe usar un entorno de edición que resalte o coloree los miembros para hacerlos 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;
}
}

Además, las personas aprenden rápidamente a ignorar el prefijo (o sufijo) para ver la parte significativa del nombre. Cuanto más leemos el código, menos vemos los prefijos. Finalmente, los prefijos se vuelven desorden invisible y un marcador de código antiguo.

Niranjan Singh
fuente
4

Esta es una vieja pregunta, pero voy a publicar aquí de todos modos. Tengo más de 20 años de programación y manejo del código de otras personas.

Creo que nombrar su variable con una breve indicación de su alcance es realmente muy útil para la siguiente persona (o usted) que mirará su código.

Uno ya no mira el código en un IDE con bonitos colores (y no puedo recordar qué significan los colores y diferentes IDE muestran diferentes colores, etc.).

Es cierto que los métodos deben ser lo suficientemente cortos como para que no estén cargados de toneladas de variables y toneladas de código, sino incluso en uno corto: cuando se mira el código que es totalmente desconocido, a veces es difícil saber si una variable es una variable de clase, local parámetro variable o método.

Poder distinguir de un vistazo hace que sea muy fácil revisar el código con el que no está familiarizado.

Toma este ejemplo:

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());
}

Ahora, tómese un tiempo y mire el código (extraído de ElasticsearchTemplate del proyecto spring-data-elasticsearch, el código que estaba revisando que me impulsó a buscar en Google lo que la gente dice sobre las convenciones de nomenclatura).

  • ¿Cuál es el puntaje de resultsMapper?
  • Es requestBuildingun parámetro?
  • etc ...

Aquí está mi sugerencia simple sobre cómo se deben nombrar las variables:

  • Atributos estáticos de clase (es decir, constantes): ALL_CAPS_WITH_UNDERSCORES (por ejemplo HOST_NAME).
  • Atributos de clase (es decir, variables de instancia de clase): camelCase (por ejemplo resultsMapper).
  • Parámetros del método: con el prefijo a(por ejemplo aQuery, aClazz).
  • Variables locales: prefijadas con my(por ejemplo myIndexName, myType).

El código anterior se convierte en:

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());

}

¿Eso es perfecto? No lo creo. Pero lo anterior, en lo que respecta a las variables, ahora es más fácil de leer. Hay otras cosas, como la alineación y el espaciado, que no abordaré en esta respuesta, ya que no está relacionada con la pregunta, lo que facilitaría también la lectura.

¿No te gusta Camel Case? Bien, use guiones bajos, etc., pero prefija sus variables locales y sus parámetros para hacerlos diferentes a las variables de instancia de clase.

No le gusta ay my, bueno, simplemente manténgase constante dentro de su proyecto y use otra cosa ... pero use algo.

Regla # 1: consistencia dentro del proyecto.

Regla # 2: facilite la lectura y no requiera que el lector sepa todo antes de poder aprender.

ETL
fuente
3

Esto es en gran medida una cuestión de preferencia, y como tal no hay una respuesta "correcta". Entonces, esta pregunta en realidad podría estar cerrada. Pero antes de que lo haga, déjame decirte que estoy totalmente de acuerdo contigo. Los prefijos disminuyen la visibilidad en lo que a mí respecta. Sin mencionar el hecho de que, si va a haber algún prefijo, debe usarse para cosas más útiles, como la intención original de la notación húngara , y no para cosas que su IDE pueda resaltar de todos modos.

Uso SentenceCase para datos de instancia (ya sean variables o constantes) y lower_case para parámetros y variables locales, ya que realmente hay muy poca, si alguna, diferencia entre los dos. Nunca, nunca uso headlessCamelCase porque es poco convincente : un identificador de un solo componente parece en minúsculas, incluso si estaba destinado a ser headlessCamelCase.

Mike Nakis
fuente