Este artículo enumera algunos errores típicos que veo en el código Java de mis colegas a mi alrededor. Obviamente, el análisis de código estático (nuestro equipo usa qulice) no podrá encontrar todos los problemas, por eso los enumero aquí.
Si cree que falta algo, hágamelo saber y estaré encantado de agregarlo.
Todos estos errores que se enumeran a continuación están básicamente relacionados con la programación orientada a objetos, especialmente la programación orientada a objetos de Java.
nombre de clase
Lea este breve artículo "¿Qué son los objetos?". Una clase debe ser una entidad abstracta en la vida real, no "validadores", "controladores" y "administradores". Si el nombre de su clase termina en "er", es un mal diseño.
Por supuesto, las clases de herramientas también son antipatrones, como StringUtils, FileUtils e IOUtils de Apache. Todo lo anterior son ejemplos de mal diseño. Lectura adicional: Alternativas a las clases de herramientas en programación orientada a objetos.
Por supuesto, no utilice prefijos ni sufijos para distinguir clases de interfaces. Por ejemplo, estos nombres son incorrectos: IRecord, IfaceEmployee o RecordInterface. En términos generales, el nombre de la interfaz debe ser el nombre de la entidad de la vida real y el nombre de la clase debe describir los detalles de su implementación. Si no hay nada especial en la implementación, puede llamarla Predeterminada, Simple o algo similar. Por ejemplo:
Copie el código de código de la siguiente manera:
clase SimpleUser implementa Usuario {};
clase DefaultRecord implementa Registro {};
clase Implementos con sufijo Nombre {};
clase Validado implementa Contenido {};
nombre del método
Los métodos pueden devolver valores o anularlos. Si un método devuelve un valor, su nombre debe describir lo que devuelve, por ejemplo (nunca utilice el prefijo get):
Copie el código de código de la siguiente manera:
booleano es válido (nombre de cadena);
Contenido de cadena();
int ageOf (archivo de archivo);
Si devuelve void, su nombre debería explicar lo que hace. Por ejemplo:
Copie el código de código de la siguiente manera:
guardar vacío (archivo de archivo);
proceso nulo(Trabajo trabajo);
anexar vacío (archivo de archivo, línea de cadena);
Sólo hay una excepción a las reglas que acabamos de mencionar: el método de prueba de JUnit no cuenta. Esto se discutirá a continuación.
El nombre del método de prueba.
En los casos de prueba JUnit, el nombre del método debe ser una declaración en inglés sin espacios. Quedará más claro con un ejemplo:
Copie el código de código de la siguiente manera:
/**
* HttpRequest puede devolver su contenido en Unicode.
* @throws Excepción si la prueba falla
*/
public void returnItsContentInUnicode() lanza una excepción {
}
La primera oración en su JavaDoc debe comenzar con el nombre de la clase que desea probar, seguido de can. Por lo tanto, tu primera oración debería ser algo así como "alguien puede hacer algo".
El nombre del método también es el mismo, sólo que sin el tema. Si agrego un asunto en medio del nombre del método, obtengo una oración completa, como en el ejemplo anterior: "HttpRequest devuelve su contenido en Unicode".
Tenga en cuenta que el nombre del método de prueba no comienza con can. Sólo los comentarios en JavaDoc comenzarán con can. Además, los nombres de los métodos no deben comenzar con un verbo.
En la práctica, es mejor declarar el método de prueba para lanzar una excepción.
nombre de la variable
Evite combinar nombres de variables, como timeOfDay, firstItem o httpRequest. Esto es cierto para las variables de clase y las variables dentro de los métodos. Los nombres de las variables deben ser lo suficientemente largos para evitar ambigüedades dentro de su alcance visible, pero no demasiado largos si es posible. El nombre debe ser un sustantivo en singular o plural, o una abreviatura apropiada. Por ejemplo:
Copie el código de código de la siguiente manera:
Listar nombres <String>;
void sendThroughProxy (archivo de archivo, protocolo de protocolo);
contenido de archivo privado;
solicitud pública HttpRequest;
A veces, si el constructor guarda los parámetros de entrada en un objeto recién inicializado, los nombres de sus parámetros y los atributos de clase pueden entrar en conflicto. En este caso, mi sugerencia es eliminar las vocales y utilizar abreviaturas.
Ejemplo:
Copie el código de código de la siguiente manera:
Mensaje de clase pública {
destinatario de cadena privada;
Mensaje público (cadena rcpt) {
este.destinatario = rcpt;
}
}
Muchas veces, puedes saber qué nombre se le debe dar a la variable mirando el nombre de su clase. Simplemente use su forma minúscula, que es confiable así:
Copie el código de código de la siguiente manera:
Archivo de archivo;
Usuario usuario;
Sucursal sucursal;
Sin embargo, nunca debes hacer esto con tipos primitivos, como números enteros o cadenas de caracteres.
Si hay múltiples variables de diferente naturaleza, considere usar adjetivos. Por ejemplo:
Copie el código de código de la siguiente manera:
Contacto de cadena (Cadena izquierda, Cadena derecha);
Constructor
Sin tener en cuenta las excepciones, solo debe utilizarse un constructor para almacenar datos en variables de objeto. Otros constructores llaman a este constructor con diferentes parámetros. Por ejemplo:
Copie el código de código de la siguiente manera:
Servidor de clase pública {
dirección de cadena privada;
Servidor público (cadena uri) {
esta.dirección = uri;
}
Servidor público (URI uri) {
this(uri.toString());
}
}
variable única
Las variables desechables deben evitarse a toda costa. Lo que quiero decir con "una sola vez" aquí es una variable que solo se usa una vez. Por ejemplo, este:
Copie el código de código de la siguiente manera:
Nombre de cadena = "datos.txt";
devolver nuevo archivo (nombre);
Las variables anteriores sólo se usan una vez, por lo que este código se puede reestructurar así:
Copie el código de código de la siguiente manera:
devolver nuevo archivo ("datos.txt");
A veces, en casos excepcionales (principalmente para lograr un formato más atractivo), se pueden utilizar variables desechables. Sin embargo, esto debe evitarse en la medida de lo posible.
anormal
No hace falta decir que nunca debes tragarte una excepción, sino que debes dejarla pasar lo más alto posible. Los métodos privados siempre deben generar excepciones marcadas.
No utilice excepciones para el control de flujo. Por ejemplo, el siguiente código es incorrecto:
Copie el código de código de la siguiente manera:
tamaño entero;
intentar {
tamaño = this.fileSize();
} captura (IOException ex) {
tamaño = 0;
}
Entonces, ¿qué debe hacer si IOException indica "El disco está lleno"? ¿Seguiría pensando que el tamaño del archivo es 0 y continuaría procesando?
sangría
Con respecto a la sangría, la regla principal es que el corchete de apertura termina al final de la línea o se cierra en la misma línea (lo contrario ocurre con los corchetes de cierre). Por ejemplo, lo siguiente es incorrecto porque el primer corchete de apertura no está cerrado en la misma línea y hay otros caracteres después. El segundo corchete también es problemático porque está precedido por caracteres pero el corchete de apertura correspondiente no está en la misma línea:
Copie el código de código de la siguiente manera:
archivo final archivo = nuevo archivo (directorio,
"archivo.txt");
La sangría correcta debería ser así:
Copie el código de código de la siguiente manera:
StringUtils.join(
matrices.asList(
"primera línea",
"segunda línea",
StringUtils.join(
Arrays.asList("a", "b")
)
),
"separador"
);
La segunda regla importante sobre la sangría es que debes intentar escribir tantos caracteres como sea posible en una línea al mismo tiempo; el límite superior es 80 caracteres. El ejemplo anterior no satisface este punto y también se puede reducir:
Copie el código de código de la siguiente manera:
StringUtils.join(
matrices.asList(
"primera línea", "segunda línea",
StringUtils.join(Arrays.asList("a", "b"))
),
"separador"
);
constantes redundantes
Las constantes de clase deben usarse cuando desee compartir información dentro de los métodos de una clase. La información debe ser exclusiva de su clase. No utilice constantes como sustitutos de cadenas o literales numéricos; esta es una muy mala práctica y contaminará su código. Las constantes (como cualquier objeto en programación orientada a objetos) deberían tener su propio significado en el mundo real. Veamos qué significan estas constantes en la vida real:
Copie el código de código de la siguiente manera:
documento de clase {
cadena final estática privada D_LETTER = "D" // mala práctica
cadena final estática privada EXTENSIÓN = ".doc" // buena práctica
}
Otro error común es utilizar constantes en pruebas unitarias para evitar cadenas redundantes o literales numéricos en los métodos de prueba. ¡No hagas esto! Cada método de prueba debe tener sus propios valores de entrada únicos.
Utilice texto o valores nuevos en cada nuevo método de prueba. Son independientes entre sí. Entonces, ¿por qué siguen compartiendo las mismas constantes de entrada?
Acoplamiento de datos de prueba
A continuación se muestra un ejemplo de acoplamiento de datos en un método de prueba:
Copie el código de código de la siguiente manera:
Usuario usuario = nuevo Usuario ("Jeff");
// tal vez algún otro código aquí
MatcherAssert.assertThat(usuario.nombre(), Matchers.equalTo("Jeff"));
En la última línea, "Jeff" está acoplado al mismo literal de cadena en la primera línea. Si después de unos meses alguien quiere cambiar el valor en la tercera línea, tendrá que dedicar tiempo a descubrir dónde también se usa "Jeff" con el mismo método.
Para evitar esto, será mejor que introduzcas una variable.