CodeGym /Blog Java /Random-ES /Reglas de codificación: el poder de los nombres correctos...
John Squirrels
Nivel 41
San Francisco

Reglas de codificación: el poder de los nombres correctos, los buenos y los malos comentarios

Publicado en el grupo Random-ES
Reglas de codificación: el poder de los nombres correctos, los buenos y los malos comentarios - 1¿Con qué frecuencia ha tenido que indagar en el código de otra persona? En lugar de dos horas, puede pasar dos días simplemente para comprender la lógica de lo que está sucediendo. Lo curioso es que para la persona que escribió el código, todo es claro y totalmente transparente. Esto no es sorprendente: después de todo, el código perfecto es un concepto muy vago, porque cada desarrollador tiene su propia visión del mundo y del código también. Más de una vez he estado en una situación en la que un compañero de trabajo y yo miramos el mismo código y teníamos diferentes opiniones sobre su corrección y limpieza.Reglas de codificación: el poder de los nombres correctos, buenos y malos comentarios - 2Suena familiar, ¿no? Aún así, hay algunos principios probados en el tiempo que deben cumplirse. Al final, serán ventajosos para usted, porque si deja su código en el estado en que le gustaría recibirlo, el mundo se volvería un poco más feliz y limpio. En nuestro artículo anterior(o más bien, una pequeña guía) sobre reglas de codificación, obtuvimos algunas recomendaciones para escribir un sistema como un todo y sus partes constituyentes, como objetos, interfaces, clases, métodos y variables. En ese mismo artículo, casualmente mencioné la correcta denominación de ciertos elementos. Me gustaría hablar de esto hoy, porque los nombres correctos hacen que el código sea mucho más fácil de leer. Cerraremos el tema del código correcto con algunas reflexiones, pequeños ejemplos de comentarios en código, y una consideración de si esto es bueno o no tan bueno. Bueno, comencemos.

nombres correctos

Los nombres correctos mejoran la legibilidad del código, lo que reduce el tiempo necesario para familiarizarse con el código, ya que usar un método es mucho más fácil cuando su nombre describe aproximadamente su funcionalidad. Todo en el código consta de nombres (variables, métodos, clases, objetos, archivos, etc.), por lo que este punto se vuelve muy importante al crear un código correcto y limpio. Según lo anterior, el nombre debe transmitir un significado, por ejemplo, por qué existe la variable, qué hace y cómo se usa. Señalaré más de una vez que el mejor comentario para una variable es darle un buen nombre.Reglas de codificación: el poder de los nombres correctos, buenos y malos comentarios - 3

de la serie de televisión "Sherlock" (2010-2017)

Nombrando interfaces

Las interfaces suelen tener nombres que comienzan con una letra mayúscula y se escriben en CamelCase. Al escribir una interfaz, solía considerarse una buena práctica agregar el prefijo "I" para designarla como una interfaz (por ejemplo, IUserService), pero eso se ve bastante feo y distrae. En tales casos, es mejor omitir el prefijo (UserService) y agregar "Impl" como sufijo al nombre de su implementación (por ejemplo, UserServiceImpl). O posiblemente, como último recurso, agregue un prefijo "C" al nombre de la implementación (por ejemplo, CUserService).

nombres de clases

Al igual que las interfaces, los nombres de las clases están en mayúscula y usan CamelCase. No importa si nos enfrentamos a un apocalipsis zombi, no importa si el final está cerca, ¡nunca, nunca, nunca el nombre de una clase debe ser un verbo! Los nombres de clases y objetos deben ser sustantivos o compuestos (UserController, UserDetails, UserAccount, etc.). No debe agregar la abreviatura de la aplicación al final del nombre de cada clase, ya que eso solo agregaría una complejidad innecesaria. Por ejemplo, si tenemos una aplicación de migración de datos de usuario, no agregue "UDM" a cada clase, es decir, UDMUserDetails, UDMUserAccount, UDMUserController.

nombres de métodos

Por lo general, los nombres de los métodos comienzan con una letra minúscula, pero también usan el estilo de mayúsculas y minúsculas (camelCase). Arriba, dijimos que los nombres de las clases nunca deberían ser verbos. Aquí la situación es todo lo contrario: los nombres de los métodos deben ser verbos o frases verbales: findUserById, findAllUsers, createUser, etc. Al crear un método (así como variables y clases), utilice una convención de nomenclatura coherente para evitar confusiones. Por ejemplo, para encontrar un usuario, un método podría llamarse getUserById o findUserById. Y una cosa más: no uses el humor en los nombres de los métodos, porque los demás pueden no entender el chiste. Como resultado, es posible que no comprendan lo que hace el método.

Nombres de variables

En la mayoría de los casos, los nombres de las variables comienzan con una letra minúscula y también usan camelCase, excepto cuando la variable es una constante global. En tales casos, todas las letras del nombre se escriben en mayúsculas y las palabras se separan con un guión bajo ("_"). Para mayor comodidad, puede usar un contexto significativo al nombrar variables. En otras palabras, cuando una variable existe como parte de algo más grande, por ejemplo, firstName, lastName o status. En tales casos, puede agregar un prefijo que indique el objeto al que pertenece esta variable. Por ejemplo: nombre de usuario, apellido de usuario, estado de usuario. También debe evitar nombres similares para las variables cuando tienen significados completamente diferentes. Aquí hay algunos antónimos que se encuentran con frecuencia y que se usan en nombres de variables:
  • empezar/finalizar
  • primero último
  • bloqueado/desbloqueado
  • mínimo máximo
  • siguiente anterior
  • viejo nuevo
  • abierto/cerrado
  • visible invisible
  • origen Destino
  • origen Destino
  • arriba abajo

Nombres cortos de variables

Cuando tenemos variables como x o n o algo así, no vemos inmediatamente la intención de la persona que escribió el código. No es obvio lo que hace n. Descubrir eso requiere una contemplación más cuidadosa (y esto significa tiempo, tiempo, tiempo). Por ejemplo, supongamos que tenemos un campo que representa el id del usuario responsable. En lugar de algún nombre de variable como x o simplemente id, llamaremos a esta variable "responsibleUserId", lo que mejora inmediatamente la legibilidad y el contenido de la información. Dicho esto, los nombres cortos como n tienen un lugar como variables locales en métodos pequeños, donde el bloque de código que involucra esta variable tiene solo un par de líneas y el nombre del método describe perfectamente lo que sucede allí. Al ver tal variable, un desarrollador entiende que es de importancia secundaria y tiene un alcance muy limitado. Como resultado, el alcance tiene una cierta dependencia de la longitud del nombre de una variable: cuanto más largo sea el nombre, más global será la variable y viceversa. Como ejemplo, aquí hay un método para encontrar el último usuario guardado por fecha:

public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
Aquí usamos variables de nombre abreviado x e y para ordenar la secuencia, y luego nos olvidamos de ellas.

Longitud óptima

Continuemos con el tema de la longitud del nombre. La longitud óptima del nombre se encuentra entre n y maximumNumberOfUsersInTheCurrentGroup. En otras palabras, los nombres cortos sufren de falta de significado, mientras que los nombres que son demasiado largos alargan el programa sin agregar legibilidad, y simplemente somos demasiado perezosos para escribirlos cada vez. Aparte del caso descrito anteriormente para las variables con un nombre corto como n, debe ceñirse a una longitud de entre 8 y 16 caracteres. Esta no es una regla estricta, solo una guía.

Pequeñas diferencias

No puedo dejar de mencionar sutiles diferencias en los nombres. Esta también es una mala práctica, ya que estas diferencias pueden ser simplemente confusas o requerir mucho tiempo adicional para notarlas. Por ejemplo, la diferencia entre InvalidDataAccessApiUsageException e InvalidDataAccessResourceUsageException es difícil de detectar de un vistazo. A menudo, también puede surgir confusión cuando se usan L y O minúsculas, porque pueden confundirse fácilmente con 1 y 0. En algunas fuentes, la diferencia es más obvia, en otras, es menor.

El significado

Necesitamos hacer que los nombres sean significativos, pero no crear ambigüedad a través de sinónimos, ya que, por ejemplo, UserData e UserInfo en realidad tienen el mismo significado. En este caso, tendríamos que profundizar en el código para comprender qué objeto en particular necesitamos. Evite palabras que no transmitan información útil. Por ejemplo, en firstNameString, ¿por qué necesitamos la palabra String? ¿Podría esto realmente ser un objeto Fecha? Por supuesto que no. Entonces, simplemente usamos firstName. También me gustaría mencionar las variables booleanas. Como ejemplo, tome un booleano llamado flagDeleted. La palabra bandera no tiene significado. Es más razonable llamarlo isDeleted.

Desinformación

También me gustaría decir algunas palabras sobre las convenciones de nomenclatura incorrectas. Digamos que tenemos una variable llamada userActivityList, pero en lugar de ser una Lista, este objeto es otro tipo de contenedor u objeto de almacenamiento personalizado. Esto podría confundir al programador promedio: es mejor llamarlo algo así como userActivityGroup o userActivities.

Buscar

Uno de los inconvenientes de los nombres cortos y simples es que son difíciles de encontrar en una gran cantidad de código. ¿Cuál sería más fácil de encontrar: "nombre" o "NOMBRE_PARA_USUARIO_DEFAULT"? La segunda opción, por supuesto. Debemos evitar las palabras (letras) encontradas con frecuencia en los nombres, ya que solo aumentarán la cantidad de archivos coincidentes durante una búsqueda, lo cual no es bueno. Me gustaría recordarle que los programadores dedican más tiempo a leer el código que a escribirlo, así que sea inteligente al nombrar los elementos de su aplicación. Pero, ¿y si no se puede encontrar un buen nombre? ¿Qué pasa si el nombre de un método no describe bien su funcionalidad? Aquí es donde los comentarios entran en escena.

Comentarios

Reglas de codificación: el poder de los nombres correctos, buenos y malos comentarios - 4No hay nada mejor que un comentario pertinente, pero nada abarrota un módulo como comentarios vacíos, desactualizados o falsos. Pueden ser un arma de doble filo, ¿no? Aún así, no debe tratar los comentarios como inequívocamente buenos, sino como un mal menor. Después de todo, un comentario es esencialmente una forma de compensar el pensamiento que no se refleja claramente en el código. Por ejemplo, los usamos para transmitir de alguna manera la esencia de un método, si el método en sí resulta demasiado confuso. En esta situación, es mejor refactorizar correctamente el código que escribir notas descriptivas. Cuanto más antiguo es el comentario, peor es el comentario, porque el código tiende a crecer y evolucionar, pero los comentarios pueden seguir siendo los mismos. Cuanto más tiempo haya pasado desde que se creó un comentario, más cuestionable puede ser. Los comentarios inexactos son mucho peores que no hacer ningún comentario, porque son confusos y engañosos, y dan falsas expectativas. E incluso si tenemos un código muy complicado, deberíamos reescribirlo en lugar de comentarlo.

tipos de comentarios

  • Comentarios legales — Comentarios al principio de cada archivo fuente por razones legales, por ejemplo:

    
    * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
    

  • Comentarios informativos : comentarios que representan una explicación del código (que brindan información adicional o explican la intención de una determinada sección del código).

    Por ejemplo:

    
    /*
    * Combines the user from the database with the one passed for updating
    * When a field in requestUser is empty, it is filled with old data from foundUser
    */
    private User mergeUser(User requestUser, User foundUser) {
           return new User(
           foundUser.getId(),
           requestUser.getFirstName() == null ? requestUser.getFirstName() : foundUser.getFirstName(),
           requestUser.getMiddleName() == null ? requestUser.getMiddleName() : foundUser.getMiddleName(),
           requestUser.getLastName() == null ? requestUser.getLastName() : foundUser.getLastName(),
           requestUser.getAge() == null ? requestUser.getAge() : foundUser.getAge()
           );
           }
    

    En este caso, puede prescindir de los comentarios, ya que el nombre del método y sus parámetros, junto con una funcionalidad muy transparente, se describen bien.

  • Comentarios de advertencia : comentario destinado a advertir a otros desarrolladores sobre las consecuencias no deseadas de una acción (por ejemplo, advertirles sobre por qué una prueba se marcó como @Ignorar):

    
    // Takes too long to run
    // Don't run if you don't have a lot of time
    @Ignore
    @Test
    public void someIntegrationTest() {
           ……
           }
    

  • TODO : comentarios que son una nota sobre algo que se debe hacer en el futuro pero que, por alguna razón, no se puede hacer ahora. Esta es una buena práctica, pero dichos comentarios deben revisarse regularmente para eliminar los irrelevantes y evitar el desorden.

    Un ejemplo sería:

    
    // TODO: Add a check for the current user ID (when the security context is created)
    
    @Override
    public Resource downloadFile(File file) {
           return fileManager.download(file);
           }
    

    Aquí notamos el hecho de que necesitamos agregar una comparación del usuario que realiza la operación de descarga (cuya ID extraeremos del contexto de seguridad) con el que realizó la operación de guardar.

  • Comentarios de refuerzo — Comentarios que enfatizan la importancia de una circunstancia que a primera vista puede parecer insignificante.

    Como ejemplo, considere una parte de un método que llena una base de datos de prueba con algunos scripts:

    
    Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8)
           .trim()
           .split(";"))
           .forEach(jdbcTemplate::update);
    // The trim() call is very important. It removes possible spaces at the end of the script
    // so that when we read and split into separate requests, we don't end up with empty ones
    

  • Comentarios de Javadoc : comentarios que describen la API para ciertas funciones. Probablemente sean los comentarios más útiles, ya que es mucho más fácil trabajar con la API documentada. Dicho esto, también pueden estar desactualizados como cualquier otro tipo de comentario. Por lo tanto, nunca olvide que la principal contribución a la documentación no la realizan los comentarios, sino un buen código.

    Aquí hay un ejemplo de un método bastante común para actualizar un usuario:

    
    /**
    * Updates the passed fields for a user based on its id.
         *
    * @param id id of the user to be updated
    * @param user user with populated fields for updating
    * @return updated user
    */
           User update(Long id, User user);
    

malos comentarios

  • comentario de murmullo : comentarios que generalmente se escriben con prisa y cuyo significado solo es comprensible para el desarrollador que los escribió, ya que solo él o ella percibe la situación matizada a la que se refiere el comentario.

    Considere este ejemplo:

    
    public void configureSomeSystem() {
           try{
           String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE);
           FileInputStream stream = new FileInputStream(configPath);
           } catch (FileNotFoundException e) {
           // If there is no configuration file, the default configuration is loaded 
          }
    }
    

    ¿Quién carga estos ajustes? ¿Ya se cargaron? ¿Se supone que este método detecta excepciones y carga la configuración predeterminada? Surgen demasiadas preguntas que solo pueden responderse profundizando en una investigación de otras partes del sistema.

  • Comentarios redundantes : comentarios que no tienen ninguna carga semántica, ya que lo que sucede en una sección determinada del código es muy claro. En otras palabras, el comentario no es más fácil de leer que el código.

    Veamos un ejemplo:

    
    public class JdbcConnection{
    public class JdbcConnection{
       /**
        * The logger associated with the current class
        */
       private Logger log = Logger.getLogger(JdbcConnection.class.getName());
    
       /**
        * Creates and returns a connection using the input parameters
        */
       public static Connection buildConnection(String url, String login, String password, String driver) throws Exception {
           Class.forName(driver);
           connection = DriverManager.getConnection(url, login, password);
           log.info("Created connection with db");
           return connection;
       }
    

    ¿Cuál es el punto de tales comentarios? Todo lo que explican ya está perfectamente claro.

  • Comentarios no confiables : comentarios que son falsos y solo engañosos (desinformación). Por ejemplo, aquí hay uno.

    
    /**
    * Helper method. Closes the connection with the scanner if isNotUsing is true
    */
    private void scanClose(Scanner scan, boolean isNotUsing) throws Exception {
       if (!isNotUsing) {
           throw new Exception("The scanner is still in use");
       } scan.close();
    }
    

    ¿Qué tiene de malo este comentario? El hecho de que nos mienta un poco, en que la conexión se cierra si isNotUsing es falso, no al revés, como nos informa el comentario.

  • Comentarios obligatorios : comentarios que se consideran obligatorios (por ejemplo, comentarios de Javadoc), pero que, de hecho, a veces se acumulan en exceso y son poco confiables e innecesarios (debe pensar si estos comentarios son realmente necesarios).

  • Ejemplo:

    
    /**
    * Create a user based on the parameters
    * @param firstName first name of the created user
    * @param middleName middle name of the created user
    * @param lastName last name of the created user
    * @param age age of the created user
    * @param address address of the created user
    * @return user that was created
    */
    User createNewUser(String firstName, String middleName, String lastName, String age, String address);
    

    ¿Sería capaz de entender lo que hace el método sin estos comentarios? Lo más probable es que sí, por lo que los comentarios no tienen sentido aquí.

  • Comentarios de registro : comentarios que a veces se agregan al comienzo de un módulo cada vez que se edita (algo así como un registro de cambios).

    
    /**
    * Records kept since January 9, 2020;
    **********************************************************************
    * 9 Jan 2020: Providing a database connection using JDBC Connection;
    * 15 Jan 2020: Adding DAO-level interfaces for working with the database;
    * 23 Jan 2020: Adding integration tests for the database;
    * 28 Jan 2020: Implementation of DAO-level interfaces;
    * 1 Feb 2020: Development of interfaces for services,
    * in accordance with the requirements specified in user stories;
    * 16 Feb 2020: Implementation of service interfaces
    * (implementation of business logic related to the work of the database);
    * 25 Feb 2020: Adding tests for services;
    * 8 Mar 2020: Celebration of International Women's Day (Terry is drunk again);
    * 21 Mar 2020: Refactoring the service layer;
    */
    

    Este enfoque alguna vez estuvo justificado, pero con la llegada de los sistemas de control de versiones (por ejemplo, Git), se convirtió en un desorden innecesario y en una complicación del código.

  • Comentarios de autoría : comentarios cuyo propósito es indicar a la persona que escribió el código, para que pueda contactarlo y discutir cómo, qué y por qué, por ejemplo:

    
    * @author Bender Bending
    

    Una vez más, los sistemas de control de versiones recuerdan exactamente quién agregó cualquier fragmento de código y cuándo, por lo que este enfoque es superfluo.

  • Código comentado : código que se comentó por un motivo u otro. Este es uno de los peores hábitos, porque lo que sucede es que comentas algo y lo olvidas, y luego otros desarrolladores simplemente no tienen el coraje de eliminarlo (después de todo, ¿y si es algo valioso?).

    
    //    public void someMethod(SomeObject obj) {
    //    .....
    //    }
    

    Como resultado, el código comentado se acumula como basura. En ningún caso debe dejar dicho código. Si realmente lo necesitas, no te olvides del sistema de control de versiones.

  • Comentarios no obvios : comentarios que describen algo de una manera excesivamente complicada.

    
    /*
        * Start with an array large enough to store
        * all the data bytes (plus filter bytes) with a cushion, plus 300 bytes
        * for header data
        */
    this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];
    

    Un comentario debe explicar el código. No debería necesitar una explicación. Entonces, ¿qué está mal aquí? ¿Qué son los "bytes de filtro"? ¿De qué se trata ese "+ 1"? ¿Por qué exactamente 300?

Si ya te has decidido a escribir comentarios, aquí hay un par de consejos:
  1. Use estilos que sean fáciles de mantener: mantener estilos que son demasiado elegantes y exóticos es molesto y requiere mucho tiempo.
  2. No utilice comentarios de final de línea que se refieran a líneas individuales: el resultado es una gran cantidad de comentarios. Además, es difícil pensar en un comentario significativo para cada línea.
  3. Cuando redacte un comentario, intente responder a la pregunta "por qué", no "cómo".
  4. Evite la información abreviada. Como dije anteriormente, no necesitamos una explicación para un comentario: el comentario en sí mismo es la explicación.
  5. Puede usar comentarios para anotar unidades y rangos de valores.
  6. Coloque los comentarios cerca del código que describen.
Finalmente, todavía quiero recordarles que el mejor comentario no es ningún comentario, sino el uso de nombres hábiles en toda su aplicación. Por regla general, la mayor parte del tiempo trabajaremos con código existente, manteniéndolo y ampliándolo. Es mucho más conveniente cuando este código es fácil de leer y comprensible, ya que el código incorrecto es un obstáculo. Es como tirar una llave inglesa en las obras, y la prisa es su fiel compañera. Y cuanto más código malo tenemos, más cae el rendimiento. Esto significa que necesitamos refactorizar de vez en cuando. Pero si desde el principio intentas escribir código que no haga que los próximos desarrolladores quieran encontrarte y matarte, entonces no necesitarás refactorizarlo con tanta frecuencia. Pero seguirá siendo necesario, ya que las condiciones y requisitos del producto cambian constantemente con la adición de nuevas dependencias y conexiones. Bueno, supongo que eso es todo para mí hoy. Gracias a todos los que leyeron hasta aquí :)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION