Un breve resumen de las mejores prácticas de codificación Java

basado en estándares de codificación de Oracle, Google, Twitter y Spring Framework

El objetivo de este artículo es brindarle un resumen rápido de lo que debe y no debe hacer, en otras palabras, prefiere y evita según los estándares de codificación de gigantes tecnológicos como Oracle, Google, Twitter y Spring Framework.

Puede o no estar de acuerdo con algunas de las mejores prácticas presentadas aquí, y eso está absolutamente bien siempre que exista algún estándar de codificación.

¿Por qué codificar estándares en primer lugar? Hay muchas buenas razones si lo buscas en Google y te dejaré con la siguiente ilustración

El documento de estándares de codificación puede ser largo y aburrido. Este artículo recoge algunas partes de las convenciones de codificación de Google, Oracle, Twitter y Spring y su objetivo es proporcionarle un conjunto de prácticas fáciles de seguir y menos aburridas para que su código sea fácil de leer y mantener.

Casi siempre te unirás a equipos que trabajan en el software existente y hay muchas posibilidades de que la mayoría de los autores se hayan ido o hayan cambiado a diferentes proyectos, dejándote varado con porciones de código que te hacen cuestionar a la humanidad.

Veamos las mejores prácticas de varios estándares de codificación.

Java Source File

Las siguientes prácticas se consideran las mejores prácticas cuando se trata de archivos fuente de Java:

  • La longitud del archivo de origen es inferior a 2.000 líneas de código.
  • El archivo de origen se organiza con comentarios de documentación, declaración de paquete, seguido de un comentario de clase, importaciones agrupadas (estática última), firma de clase / interfaz, etc., como se muestra a continuación
paquete com.example.model;
/ **
 * Perspectiva libre de implementación para ser leída por los desarrolladores
 * que no necesariamente tiene el código fuente a mano
 * *
 * @ autor x, y, z
 * @fecha
 * @versión
 * @copyright
 * *
 * /
import com.example.util.FileUtil;
/ *
 * Comentario específico de clase opcional
 * *
 * /
clase pública SomeClass {
  // Variables estáticas en orden de visibilidad
  público estático final Entero PUBLIC_COUNT = 1;
  entero final estático PROTECTED_COUNT = 1;
  privado estático final Entero PRIVATE_COUNT = 1;
  // Variables de instancia en orden de visibilidad
  nombre de cadena pública;
  String postalCode;
  dirección de cadena privada;
  // Constructor y sobrecargado en orden secuencial
  public SomeClass () {}
  public SomeClass (String name) {
    this.name = nombre;
  }
  // Métodos
  public String doSomethingUseful () {
    volver "Algo útil";
  }
  // getters, setters, equals, hashCode y toString al final
}

Nombrar

Los nombres de clase e interfaz son CamelCase y se recomienda usar la palabra completa y evitar siglas / abreviaturas. Por ejemplo, class Raster o class ImageSprite

  • Paquete: nombres com.deepspace sobre com.deepSpace o com.deep_space
  • Archivo: los nombres son CamelCase y terminan con .java que coincide con el nombre de la clase. Hay una clase pública por archivo con cada clase de nivel superior en su archivo
  • Método: los nombres deben ser verbos en mayúsculas y minúsculas con cada palabra interna en mayúscula, por ejemplo run (); o runFast ();
  • Constantes: deben estar en mayúsculas con “_” separando cada palabra, por ejemplo int MIN_WIDTH = 44; e int MAX_WIDTH = 99;
  • Variable: un nombre que le dice al lector del programa qué representa la variable, es decir, si está almacenando una calificación de prueba, elija la calificación vs var1. Mantenga los nombres de las variables cortos, evite incluir metadatos.
// Preferir (): nombres de variables cortos y describir lo que almacena
int schoolId;
int [] filterSchoolIds;
int [] uniqueSchooldIds;
Map  usersById;
Valor de cadena;
// Evitar (x): nombres de variables demasiado detallados
int schoolIdentificationNumber;
int [] userProvidedSchoolIds;
int [] schoolIdsAfterRemovingDuplicates;
Map  idToUserMap;
String valueString;

Recuerde: el nombre de la variable debe ser corto y decirle fácilmente al lector qué valor representa. Usa tu juicio.

Prefiere y evita

El formateo y la sangría tienen que ver con la organización de su código para que sea fácil de leer, e incluye el espaciado, la longitud de la línea, las envolturas y los saltos, etc.

  • Sangría: use 2 o 4 espacios y manténgase constante
  • Longitud de línea: hasta 70 a 120 caracteres dependiendo del efecto en la legibilidad. Es importante eliminar la necesidad de desplazamiento horizontal y colocar saltos de línea después de una coma y un operador.

Métodos: aquí hay una lista de las mejores prácticas

// Preferir () Los saltos de línea son arbitrarios y se rompen después de una coma.
String downloadAnInternet (Internet internet, Tubos tubos,
    Blogosfera blogs, cantidad  ancho de banda) {
  tubos.download (internet);
}
// Evite (x) argumentos del método difíciles de diferenciar al cuerpo del método
String downloadAnInternet (Internet internet, Tubos tubos,
    Blogosfera blogs, cantidad  ancho de banda) {
    tubos.download (internet);
}
// Prefiero () Agregue 8 (doble de 2 o 4) espacios para sangría profunda
horkingLongMethodName privado sincronizado estático (int anArg,
        Objeto anotherArg, String yetAnotherArg,
        Object andStillAnother) {
  ...
}
// Prefiero () Escaneo fácil y espacio de columna adicional.
public String downloadAnInternet (
    Internet internet,
    Tubos tubos
    Blogosfera blogs,
    Cantidad  ancho de banda) {
  tubos.download (internet);
  ...
}
Una prueba unitaria habría captado que

Si se verifica: la OMI que escribe código bien formateado hace que sea fácil detectar errores tipográficos y errores para el autor y los revisores del código, vea a continuación:

// Evita (x) No omitas {}
si (condición)
  declaración;
// Evita (x)
si (x <0) negativo (x);
// Evita (x)
if (a == b && c == d) {
...
}
// Prefiero ()
if ((a == b) && (c == d)) {
...
}
// Prefiero ()
if (condición) {
  declaraciones;
} else if (condición) {
  declaraciones;
} else if (condición) {
  declaraciones;
}
// Evita (x)
if ((condición1 && condición2)
    || (condición3 y condición4)
    ||! (condición5 && condición6)) {// BAD WRAPS
    Haz algo al respecto(); // HAGA ESTA LÍNEA FÁCIL DE PERDER
}
// Prefiero ()
if ((condición1 && condición2)
        || (condición3 y condición4)
        ||! (condición5 && condición6)) {
    Haz algo al respecto();
}

Operador ternario: a continuación se detallan las prácticas recomendadas.

alpha = (aLongBooleanExpression)? beta: gamma;
alpha = (aLongBooleanExpression)? beta
        : gamma;
alfa = (aLongBooleanExpression)
        ? beta
        : gamma;

Cambiar: cuando se trata de cambiar, la mejor práctica es

  • Siempre tenga un caso predeterminado incluso sin código
  • Use / * cae a través de * / para indicar que el control cae al siguiente caso
interruptor (condición) {
  caso ABC:
    declaraciones;
  /* cae a través */
  caso DEF:
    declaraciones;
    rotura;
  defecto:
    declaraciones;
     rotura;
}

Mensajes de excepción: al lanzar una excepción, aquí hay muestras de mensajes sangrados y mal sangrados.

// Evitar (x): no es fácil de leer
lanzar nueva IllegalStateException ("Error al procesar la solicitud" + request.getId ()
    + "para usuario" + user.getId () + "query: '" + query.getText ()
    + "'");
// Preferir () - Bastante más fácil de leer
lanzar nueva IllegalStateException ("Error al procesar"
    + "solicitud" + request.getId ()
    + "para el usuario" + user.getId ()
    + "query: '" + query.getText () + "'");

Iteradores y secuencias: las secuencias se están volviendo más comunes y, a veces, pueden ser muy complejas, por lo tanto, es importante sangrar para que sean fáciles de leer.

// Evitar (x): no es fácil de leer
Iterable  modules = ImmutableList.  builder (). Add (new LifecycleModule ())
    .add (nuevo AppLauncherModule ()). addAll (application.getModules ()). build ();
// Preferir () - Bastante más fácil de leer
Iterable  modules = ImmutableList.  builder ()
    .add (nuevo LifecycleModule ())
    .add (nuevo AppLauncherModule ())
    .addAll (application.getModules ())
    .construir();
Simplemente siga un estándar de codificación, cualquiera realmente

Declaraciones y asignaciones: se recomienda una declaración por línea, ya que fomenta los comentarios como se muestra a continuación.

// Prefiero ()
nivel int; // nivel de sangría
int sizeMeter; // tamaño de la mesa
// Evita (x) a favor de lo anterior
int level, sizeMeter;
// Preferir (): incluye la unidad en el nombre o tipo de variable
largo pollIntervalMs;
int fileSizeGb;
Cantidad  fileSize;
// Evita (x) mezclar tipos
int foo, fooarray [];
// Evitar (x) - No separar con comas
Format.print (System.out, "error"), exit (1);
// Evita (x) la asignación múltiple
fooBar.fChar = barFoo.lchar = 'c';
// Evite (x) asignaciones incrustadas en un intento de aumentar el rendimiento // o guardar una línea. Soy culpable de hacer esto :(
d = (a = b + c) + r;
// Prefiero () sobre el anterior
a = b + c;
d = a + r;
// Prefiero ()
String [] args
// Evita (x)
Args de cuerda []
// Prefiero () Usa largamente "L" en lugar de "l" para evitar confusiones con 1
tiempo de espera largo = 3000000000L;
// Evita (x): es difícil decir que la última letra es l y no 1
tiempo de espera largo = 3000000000l;

Ponga declaraciones solo al comienzo de los bloques (Un bloque es un código rodeado de llaves {y}). No espere para declarar variables hasta su primer uso; puede confundir al programador incauto y obstaculizar la portabilidad del código dentro del alcance.

// Preferir () declarar al comienzo del bloque.
public void doSomething () {
  int lo que representa; // comienzo del bloque de método
  if (condición) {
    int someFlag; // comienzo del bloque "si"
    ...
  }
}

También es importante evitar las declaraciones locales que ocultan las declaraciones de los niveles superiores y evitar confusiones como se muestra a continuación.

int cuenta;
...
public void doSomething () {
  if (condición) {
    int cuenta; // ¡EVITAR!
    ...
  }
  ...
}

Espaciado y saltos de línea: evite la tentación de guardar 1–2 líneas de código a expensas de la legibilidad. Aquí están todas las mejores prácticas cuando se trata de espacios y líneas en blanco (un espacio en blanco hace la diferencia)

  • Una (1) línea en blanco entre los métodos y los desarrolladores de Spring recomienda dos (2) líneas en blanco después de los constructores, bloque estático, campos y clase interna
  • Operadores de teclas espaciales, es decir, usar int foo = a + b + 1; sobre int foo = a + b + 1;
  • Separe todos los operadores binarios excepto "." De los operandos usando un espacio
  • La llave abierta "{" aparece al final de la misma línea que la declaración o método de declaración y la llave de cierre "}" comienza una línea por sí misma sangrada
// Preferir () - Espacio después de "while" y antes de "("
while (verdadero) {
  ...
}
// Evitar (x): a diferencia de arriba no hay espacio
while (verdadero) {
  ...
}
// Preferir () - No hay espacio entre "doSomething" y "("
public void doSomething () {
  ...
}
// Evitar (x): a diferencia del espacio anterior
public void doSomething () {
  ...
}
// Preferir (): agrega un espacio después de un argumento
public void doSomething (int a, int b) {
  ...
}
// Preferir () - Espacio entre operando y operadores (es decir, +, =)
a + = c + d;
a = (a + b) / (c * d);
while (d ++ = s ++) {
  n ++;
}

Documentación y comentarios

Vale la pena mencionar que casi todo el código cambia a lo largo de su vida útil y habrá ocasiones en que usted o alguien esté tratando de descubrir qué es lo que un bloque complejo de código, un método o una clase pretende hacer a menos que se describa claramente. La realidad es casi siempre como sigue

Hay momentos en que el comentario sobre una pieza compleja de código, método, clase no agrega ningún valor ni cumple su propósito. Esto generalmente ocurre cuando se comenta por el bien de la misma.

Los comentarios deben usarse para dar una visión general del código y proporcionar información adicional que no está fácilmente disponible en el propio código. Empecemos. Hay dos tipos de comentarios.

Comentarios de implementación: están destinados a comentar el código o comentar sobre una implementación particular del código.

Comentarios de documentación: están destinados a describir la especificación del código desde una perspectiva libre de implementación para ser leída por desarrolladores que no necesariamente tienen el código fuente a mano.

La frecuencia de los comentarios a veces refleja la mala calidad del código. Cuando se sienta obligado a agregar un comentario, considere reescribir el código para que quede más claro.

Tipos de comentarios de implementación

Hay cuatro (4) tipos de comentarios de implementación como se muestra a continuación

  • Bloquear comentario: ver ejemplo a continuación
  • Comentario de línea única: cuando el comentario no es más largo que una línea
  • Comentarios finales: comentario muy corto movido al extremo derecho
  • Comentario de fin de línea: comienza un comentario que continúa hasta la nueva línea. Puede comentar una línea completa o solo una línea parcial. No debe usarse en varias líneas consecutivas para comentarios de texto; sin embargo, se puede usar en varias líneas consecutivas para comentar secciones de código.
// Bloquear comentario
/ *
 * Uso: proporciona una descripción de archivos, métodos, estructuras de datos
 * y algoritmos. Se puede usar al comienzo de cada archivo y
 * antes de cada método. Se usa para comentarios largos que no se ajustan a
 * linea sola. 1 línea en blanco para continuar después del comentario de bloque.
 * /
// comentario de una sola línea
if (condición) {
 / * Manejar la condición. * /
  ...
}
// Comentario final
si (a == 2) {
 volver VERDADERO; /* caso especial */
} más {
 return isPrime (a); / * funciona solo para un extraño * /
}
// Comentario de fin de línea
si (foo> 1) {
  // Haz un doble giro.
  ...
} más {
  falso retorno; // Explica por qué aquí.
}
// if (bar> 1) {
//
// // Haz una triple vuelta.
// ...
//}
//más
// falso retorno;

Comentarios de documentación (es decir, Javadoc)

Javadoc es una herramienta que genera documentación HTML a partir de su código Java utilizando los comentarios que comienzan con / ** y terminan con * / - consulte Wikipedia para obtener más detalles sobre cómo funciona Javadoc o simplemente siga leyendo.

Aquí hay un ejemplo de Javadoc

/ **
 * Devuelve un objeto de imagen que luego se puede pintar en la pantalla.
 * El argumento url debe especificar una {@link URL} absoluta. El nombre
 * argumento es un especificador que es relativo al argumento url.
 * 

 * Este método siempre regresa inmediatamente, ya sea que el  * la imagen existe. Cuando este applet intenta dibujar la imagen en  * la pantalla, se cargarán los datos. Las primitivas gráficas  * que dibuje la imagen se pintará gradualmente en la pantalla.  * *  * @param url una URL absoluta que proporciona la ubicación base de la imagen  * @param nombra la ubicación de la imagen, en relación con el argumento url  * @ devolver la imagen en la URL especificada  * @ver imagen  * /  public Image getImage (URL url, nombre de cadena) {         tratar {             return getImage (nueva URL (url, nombre));         } catch (MalformedURLException e) {             volver nulo;         }  }

Y lo anterior resultaría en un HTML de la siguiente manera cuando javadoc se ejecuta contra el código que tiene lo anterior

Mira aquí para más

Aquí hay algunas etiquetas clave que puede usar para mejorar la calidad de la documentación de Java generada.

@author => @author Raf
@code => {@code A  C}
@deprecated => @deprecated deprecation-message
@exception => @exception IOException lanzada cuando
@link => {@link package.class # etiqueta de miembro}
@param => descripción del nombre del parámetro @param
@return => Lo que devuelve el método
@see => @see "string" O @see  
@since => Para indicar la versión cuando se agrega un método de acceso público

Para obtener una lista completa y una descripción más detallada, consulte aquí

El estándar de codificación de Twitter desaconseja el uso de la etiqueta @author

El código puede cambiar de manos varias veces en su vida útil, y con bastante frecuencia el autor original de un archivo fuente es irrelevante después de varias iteraciones. Creemos que es mejor confiar en el historial de confirmación y en los archivos de PROPIETARIOS para determinar la propiedad de un cuerpo de código.

Los siguientes son ejemplos de cómo podría escribir un comentario de documentación que sea perspicaz como se describe en el estándar de codificación de Twitter

// Malo.
// - El documento no dice nada que la declaración del método no hizo.
// - Este es el 'documento de relleno'. Pasaría controles de estilo, pero
No ayuda a nadie.
/ **
 * Divide una cuerda.
 * *
 * @param s Una cadena.
 * @return Una lista de cadenas.
 * /
Lista  dividida (String s);
// Mejor.
// - Sabemos en qué se divide el método.
// - Todavía hay un comportamiento indefinido.
/ **
 * Divide una cadena en el espacio en blanco.
 * *
 * @param s La cadena a dividir. Una cadena {@code null} se trata como una cadena vacía.
 * @return Una lista de las partes delimitadas por espacios en blanco de la entrada.
 * /
Lista  dividida (String s);
// Excelente.
// - Cubre otro caso de borde.
/ **
 * Divide una cadena en el espacio en blanco. Caracteres en blanco repetidos
 * están colapsados.
 * *
 * @param s La cadena a dividir. Una cadena {@code null} se trata como una cadena vacía.
 * @return Una lista de las partes delimitadas por espacios en blanco de la entrada.
 * /
Lista  dividida (String s);

Es importante ser profesional cuando se trata de escribir comentarios

// Evita (x)
// Odio tanto xml / soap, ¿por qué no puede hacer esto por mí?
tratar {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}
// Prefiero ()
// TODO (Jim): oculta la validación de campo en una biblioteca.
tratar {
  userId = Integer.parseInt (xml.getField ("id"));
} catch (NumberFormatException e) {
  ...
}

Y es importante tener en cuenta no documentar el método anulado a menos que la implementación haya cambiado.

Y aquí hay algunos puntos más a tener en cuenta.

  • Evite las importaciones de comodines: como se describe en los estándares de codificación de Twitter, hace que la fuente de la clase sea menos clara. Trabajo en un equipo con una combinación de usuarios de Eclipse e IntelliJ y descubrí que Eclipse elimina las importaciones de comodines e IntelliJ lo introduce. Probablemente haya una opción para desactivarlo, solo quería señalar el valor predeterminado para los dos.
  • Utilice siempre la anotación @Override al anular
  • Fomentar el uso de @Nullable cuando un campo o método devuelve nulo
  • Haga uso de comentarios especiales para el trabajo futuro y no olvide dejar una referencia a sí mismo para que otros sepan a quién hacer su pregunta Y en lugar de adivinar, eliminarla o verificar la culpa de git para encontrar quién la agregó. Algunos IDE como Eclipse e IntelliJ también ayudan a enumerarlos para facilitar el acceso, así como un recordatorio.
// FIXME (Raf): un mensaje procesable describe lo que hay que hacer
// TODO (Raf): un mensaje procesable describe lo que hay que hacer

El juego final es escribir código que facilite la vida de futuros autores y mantenedores.

El juego final

Otros materiales de lectura relevantes

Una lista de artículos relevantes que son relevantes para escribir código que sea limpio, bien estructurado, fácil de leer y fácil de mantener. Si desea leer más, definitivamente recomiendo lo siguiente

y otra buena lista de consejos para escribir código limpio