BUENAS PRÁCTICAS Y CONVENCIONES EN JAVA - PARTE UNO - CONVENCIONES
IntroducciónEl presente documento es la primera parte de lo que esperamos junto
Gus Garsaky sea una guía para todos aquellos desarrolladores en java. Espero que les sea de utilidad y poder ayudar a todos los desarrolladores en java que aun no esten familiarizados con el concepto de buenas prácticas y convenciones.
Es importante destacar que quizás exceptuando esta primera parte, el resto del documento puede ser de utilidad para desarrolladores sin importar el lenguaje de programación utilizado.
Sin nada mas que agregar, empecemos con la parte uno, Convenciones.
CONVENCIONES
Es importante señalar que las convenciones presentadas acá están basadas en el documento
Java Code Conventions publicado por
Sun Mycrosystems el año 1997:
http://www.oracle.com/technetwork/java/codeconventions-150003.pdf
Indice1. Por qué convenciones de código.
2. Nombres de archivos.
3. Organización de archivos.
4. Indentación.
5. Comentarios.
6. Declaraciones.
7. Sentencias.
8. Espacios en blanco.
A. Convenciones de capitalización.
9. Convenciones de nombres.
10. Hábitos de programación.
1. Por qué convenciones de códigoLas convenciones de código son importantes para los programadores por múltiples razones entre ellas, las mas destacadas:
- El 80% del tiempo de vida y costo de un software esta en la manteniente de un proyecto
- Casi ningún software es mantenido de por vida por su autor original
- Las convenciones de código mejoran la legibilidad del software, lo cual permite a los ingenieros comprender el código rápidamente.
- Un software es como cualquier otro producto, y por esta misma razón debes procurar que como cualquier otro producto, este cumpla con los estándares de calidad adecuados.
2. Nombres de archivos2.1 Extensiones de los archivosLos archivos en java utilizan las siguientes exenciones:
Tipo de archivo | Extensión |
Código fuente Java | .java |
Bytecode de Java | .class |
2.2 Nombres de archivos comunesLos nombres de archivos mas utilizados son:
Tipo de archivo | Utilidad |
GNUmakefile | El nombre preferido para archivos "make". |
README | El nombre preferido para archivos que resumen los contenidos de un directorio den particular. |
3. Organización de archivosUn archivo consta de secciones que deben ser separadas por lineas en blanco y comentarios opcionales que identifiquen cada sección.
Archivos con mas de 2000 lineas son engorrosos y deben ser evitados.
3.1 Archivos fuente de JavaCada archivo de fuente de Java contiene un sola clase o interfaces publica. cuando una clase o interfaces privada esta asociada con una clase publica, se puede poner el mismo archivo que la clase publica. La clase publica debe ser la primera clase o interfaces en el archivo.
public class MyClass {
private class MyPrivateClass {
}
Los archivos fuente de Java tienen el siguiente orden:
- Comentarios de inicio.
- Sentencias package e import.
- Declaraciones de clases e interfaces.
3.1.1 Comentarios de inicioTodo código fuente debe tener comentarios de inicio que señale el nombre de la clase, versión, una aviso de copyright , y también una breve descripción del propósito de la clase en el programa.
/*
* ClassName
*
* Version Alpha 0.0.12
*
* Copyright notice
*
* This class is...
*
*/
3.1.2 Sentencias de package e importLa primera linea no-comentario de un archivo de fuente en java es la sentencia de package, posterior a esto, las sentencias de import.
package components
import javax.swing.JFrame;
import javax.swing.JPanel;
3.1.3 Declaración de clases e interfacesLa siguiente table describe las partes de la declaración de una clase o interface, en el orden que debe aparecer.
Parte de la declaración de una clase o interface | Notas |
Comentario de documentación de la clase o interface (/**...*/) | Ver la sección 5.2 para mas información |
Sentencias class o interface | |
Comentario de implementación de la clase o interface si fuera necesario (/*...*/) | Este comentario debe tener toda información necesaria para la clase o interface que no fuera apropiada para estar en los comentarios de documentación |
Variables de clase (static) | Primero las variables de clase public, luego protected, después las de nivel de package y por ultimo private. |
Variables de instancia | Primero las variables de clase public, luego protected, después las de nivel de package y por ultimo private |
Constructores | |
Métodos | Estos métodos se deben agrupar por funcionalidad más que por visión o accesibilidad. |
4. IndentaciónSe deben utilizar cuatro espacios por cada unidad de indentado. La construcción exacta del indentado (espacios vs. tabulaciones) no se especifica. Los tabuladores deben ser exactamente cada 8 espacios (no cuatro).
4.1 Longitud de líneaEvitar mas de 80 caracteres por linea, ya que no son bien manejados por muchas terminales y herramientas.
Nota: Ejemplos de uso en documentación deben ser mas breves - generalmente no mas de 70 caracteres.4.2 Ajustes de líneasCuando una expresión no quepa en una sola linea, debe ser cortada de acuerdo a los siguientes principios:
- Cortar antes de una coma.
- Cortar después de un operador.
- Preferir cortes de alto nivel (más hacia la derecha de la expresión) que de bajo nivel (más a la izquierda que la expresión.)
- Alinear la nueva linea con la primera expresión del nivel anterior.
- Si las reglas anteriores llevan a un código confuso o a código que se aplastan contra el margen derecho, indente con 8 espacios en su lugar.
Ejemplos de cortes de llamadas métodos:
myFunction(longExpression1, longExpression2, longExpression3,
longExpression4, longExpression5);
myVariable = myFunction1(longExpression1,
myFunction2(longExpression2,
longExpression3 ));
Ejemplos de cortes de operaciones aritméticas. Se prefiere el primero, ya que el salto de linea ocurre fuera de la expresión que encierra el paréntesis:
LongName1 = LongName2 * (longName3 + longName4 - longName5)
+ 4 * longName6;
LongName2 = LongName1 * (longName3 + longName4
- longName5) + 4 * longName6;
Ejemplos de indentación de declaraciones de métodos. El primer ejemplo es el caso convencional. El segundo se desplazaría la segunda y tercera linea muy a la derecha si se utiliza la sangría convencional, por lo que en vez de eso se utilizaron los 8 espacios de indentación:
/*...*/
}
private static void myMethodWithAVeryLongName (int arg1,
/*...*/
}
El ajuste de linea para sentencias if por lo general usa la regla de 8 espacios, ya que el convencional (4 espacios) dificulta la lectura del cuerpo. Por ejemplo:
//No usar esta indentacion
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) { // Malos cortes
doSomethingAboutIt(); // Hace esta linea facil de olvidar
}
//Usar esta indentacion en su lugar
if ((condition1 && condition2)
|| (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
}
//O usar esta
if ((condition1 && condition2) || (condition3 && condition4)
||!(condition5 && condition6)) {
doSomethingAboutIt();
}
Aquí tres aceptables formas de expresiones ternarias:
bool1 = (myVeryLongBooleanExpression) ? bool2 : bool3;
bool2 = (myVeryLongBooleanExpression) ? bool1
: bool3;
bool3 = (myVeryLongBooleanExpression)
? bool2
: bool1;
5. Comentarios Los programas en java tienen dos tipos de comentarios: comentarios de implementacion y comentarios de documentación. los comentarios de implementacion son los que se encuentran en C++, los cuales son los delimitados por /*...*/, y //. Los comentarios de documentación (conocidos como "doc comments") están solo en java, y son delimitados por /**....*/. los comentarios de documentación pueden ser extraídos de archivos html utilizando las herramientas de javadoc.
Los comentarios de implementación son para comentar código o para comentar una implementación en particular. los comentarios de documentado son para describir las especificaciones de el código, desde una perspectiva de implementación libre. Para ser leído por desarrolladores quienes no necesariamente tienen el código fuente en cuestión.
Los comentarios podrían ser usados para obtener descripciones del código y proveer una información adicional que no es fácilmente deducible con el propio código. Los comentarios deben contener solo información que es relevante para el lector y el entendimiento del programa. Por ejemplo, la información sobre el paquete correspondiente en que se construye o el directorio en el que se encuentra el archivo no debe ser parte de un comentario.
Decisiones no triviales o no obvias del diseño son apropiadas, pero se debe evitar la duplicación de la información que este presente y clara en el código.
Nota: la frecuencia de los comentarios a veces refleja la mala calidad del código. Cuando usted se siente obligado a insertar un comentario, considere volver a escribir el código para hacerlo mas claro.Los comentarios no deben encerrarse en grandes cajas dibujadas con asteriscos u otros caracteres.
Los comentarios nunca deben incluir caracteres espaciales.
5.1 Formato de comentarios de implementación:Los programas pueden tener cuatro estilos de implementación de comentarios: bloque, una linea, remolque y de fin de linea.
5.1.1 Comentarios de bloque:Los comentarios de bloque son usados para la descripción de archivos, métodos, estructuras de datos y algoritmos. Deben ser usados al principio de cada archivo y antes de cada método. También pueden ser usados en otros lugares como por ejemplo dentro de métodos. Los comentarios de bloques dentro de funciones o métodos deben tener una sangría al mismo nivel que el código a describir.
Un código de bloque debe ser precedido por una linea en blanco para apartarlo del resto del código.
Los comentarios de bloque tienen un asterisco "*" antes de cada linea.
/*
* Here is a block comment.
*/
Los comentarios de bloques pueden empezar con /*-, que es reconocido por el guion como un comentario de bloque que no debe ser reformado
/*
* Here is a block comment with some very special
* formatting that I want indent(1) to ignore.
*
* one
* two
* three
*/
Nota: Si no utilizas un indentado no debes utilizar /*-5.1.2. Comentarios de una sola lineaComentarios cortos pueden ser incluidos en una sola linea indentados de tal manera que se encuentre al mismo nivel del código que describen. Si un comentario no puede ser escrito en una sola linea, se debe utilizar un comentario de bloque. Un comentario de una sola linea debe ser precedido por una linea en blanco.
if (condition) {
/* Handle the condition. */
...
}
5.1.3. Comentarios de remolqueComentarios muy cortos pueden aparecer en la misma linea del código que describen, pero debe ser indentado de tal manera de que este se encuentre lejos del código. Si existe mas de un comentario con estas características, todos deben ser indentados al mismo nivel.
if (a == 2) {
return TRUE; /* special case */
} else {
return isprime(a); /* works only for odd a */
}
5.1.4. Comentarios de fin de lineaEl delimitador de comentarios // puede convertir en comentario una linea. Puede comentar una linea completa o solo parte de ella. No debe ser usado consecutivamente en múltiples lineas para textos extensos; sin embargo, se puede usar consecutivamente para comentar secciones de código.
if (foo > 1) {
// Do a double-flip.
...
}
else
return false; // Explain why here.
//if (bar > 1) {
//
// // Do a triple-flip.
// ...
//}
//else
// return false;
5.2 Comentarios de documentadoLos comentarios de documentado describen clases, interfaces, constructores, métodos y archivos. Cada comentario de documentado es creado dentro de los delimitadores /**...*/.
/**
* @param name the component name
* @return the component
*/
Java asocia los comentarios de documentado a la siguiente declaración posicionada posterior el comentario de documentado, por esta razón estos comentarios no deben ser ingresados dentro de los métodos o secciones que se intentan describir.
Nota: Los comentarios de documentado serán vistos mas afondo en otra parte independiente de este documento, por el momento para mas información: http://www.oracle.com/technetwork/articles/java/index-137868.html
6. Declaraciones6.1. Numero de declaraciones por líneaSe recomienda una declaración por línea, ya que facilita la adición de comentarios. Por esta razón se prefiere:
int level; // indentation level
int size; // size of table
Antes que:
int level, size;
No poner diferentes tipos en la misma linea. Ejemplo:
int foo, fooarray[];
Nota: Los ejemplos anteriores usaban un espacio entre el tipo y el nombre identificador. Otra alternativa aceptable es usar tabulaciones:int level;
int size;
6.2. UbicaciónLocalizar las declaraciones solo al inicio de los bloques (Los bloques son aquellos delimitados por {}). No esperar a declarar variables antes de su primer uso; esto puede llegar a ser confuso para el programador, y probablemente pueda omitir una declaración.
public void MyMethod() {
int int1; // beginning of method block
if (condition) {
int int2; // beginning of "if" block
/*...*/
}
}
La única excepción a esta regla es en caso de los ciclos for, ya que en Java se pueden declarar dentro del ciclo for
for (int i = 0; i < maxLoops; i++) {
/*...*/
}
Evitar declarar variables que tengan el mismo nombre que algún atributo de la clase que la contiene.
private int count;
public int counter(){
int count = 0;
/*...*/
}
6.3. InicializaciónIntentar inicializar las variables locales en el mismo momento en que son declaradas. A menos que no pueda ser inicializada porque su valor depende de cálculos realizados por el programa posterior a su declaración.
6.4. Declaración de clases e interfacesCuando programa en java clases e interfaces, debe seguir el siguiente formato de reglas:
- No deben haber espacios entre el nombre de un método y el paréntesis que delimita su lista de parámetros.
- Abrir llaves de apertura aparecen al final de la misma linea de la declaración de la sentencia.
- La llave de cierre empieza en una nueva linea posterior a la ultima instrucción del bloque, y al mismo nivel de indentación que la sentencia de declaración del bloque. En caso de no existir ninguna instrucción dentro del bloque, la llave de cierre se coloca inmediatamente después a la de apertura.
- Los métodos se separar con una linea en blanco.
private int ivar1;
private int ivar2;
Sample(int i, int j) {
ivar1 = i;
ivar2 = j;
}
int emptyMethod() {}
/*...*/
}
7. Sentencias7.1. Sentencias simplesCada linea debe tener solo una sentencia. El siguiente ejemplo es incorrecto.
var1++; var2--;
7.2 Composición de sentenciasLas sentencias compuestas son sentencias que contienen listas de sentencias encerradas entre llaves.
Las sentencias encerradas deben tener una indentación mas que su nivel superior
Las llaves de inicio deben estar al final de la linea que comienza la sentencia compuesta; la llave de cierre debe empezar en una nueva linea y ser indentada al mismo nivel que el principio de la sentencia compuesta.
Las llaves se usan en todas las sentencias, sin importar su simplicidad. Esto hace mas fácil añadir nuevas sentencias.
7.3. Sentencias returnUna sentencia que retorna un valor no debe usar paréntesis, amenos que hagan que el valor de retorno sea mas evidente de alguna manera.
7.4. Sentencias if, if-else, if-else-if-eseLas sentencias if-else deben seguir el siguiente formato:
if (condition) {
/* statement */
}
if (condition) {
/* statement */
} else {
/* statement */
}
if (condition) {
/* statement */
} else if (condition){
/* statement */
} else {
/* statement */
}
Nota: La sentencia if siempre debe usar llaves. con el fin de evitar errores.7.5. Sentencias forUna sentencia for debe seguir el siguiente formato:
for(initialization; condition; update) {
/* statements */
}
Una sentencia for bacía debe seguir el siguiente formato:
for(initialization; condition; update);
Si se utiliza una coma en la inicialización, condición, o actualización se debe evitar usar mas de tres variables. Si aun así, es necesario, es recomendado separar la sentencia, declarando las variables antes del bucle o actualizando en el bucle
7.6. Sentencias whileUna sentencia while debe seguir el siguiente formato:
while (condition) {
/* Statement */
}
Una sentencia while bacía debe seguir el siguiente formato
while (condition);
7.7. Sentencia do-whileUna sentencia do-while debe seguir el siguiente formato:
do {
/* statement */
} while (condition);
7.8. Sentencia switchUna sentencia switch debe seguir el siguiente formato:
switch (condition) {
case 0:
/* statement */
break;
case 2:
/* statement */
break;
default:
/* statement */
break;
}
Siempre que un caso se propague (no incluya sentencia break), añadir un comentario donde la sentencia break se encontraría normalmente.
Cada sentencia switch debe incluir un caso por defecto. El break del caso por defecto es redundante, pero evita que se propague por error si se añade otro caso.
7.9. Sentencia try-catchUna sentencia try-catch debe seguir el siguiente formato:
try {
/* statement */
/* statement */
}
8. Espacios en blanco8.1. Líneas en blancoLas lineas en blanco nos permite separar secciones de código que están lógicamente relacionadas.
Se debe usar siempre dos lineas en blanco para las siguientes circunstancias:
- Entre las secciones de un archivo fuente
- Entre las definiciones de clases e interfaces.
Se debe usar siempre un linea en blanco en las siguientes circunstancias:
- Entre métodos
- Entre las variables locales de un método y su primera sentencia
- Antes de un comentario de bloque o de un comentario de linea
- Entre las secciones logísticas de un método para facilitar la comprensión
8.2. Espacios en blancoLos espacios en blanco son usados en las siguientes circunstancias:
- Las palabras claves del lenguaje seguidas por un paréntesis deben ser separados por un espacio en blanco
Nota: no se debe usar un espacio en blanco entre el nombre de un método y su paréntesis de apertura. Esto ayuda a distinguir palabras claves de llamadas de métodos. - Debe haber un espacio en blanco después de una coma en listas de argumentos.
- Todo los operadores binarios excepto . se deben separar de sus operandos por un espacio en blanco. Los espacios en blanco no deben separar los operadores de incremento (++) y decremento (--) de sus operandos.
- Los casting deben ir seguidos de un espacio en blanco.
A. Convenciones de capitalizaciónNota: Esta sección ha sido a agregada con el propósito de facilitar la comprensión de la sección 9. Convenciones de nombresMuchas de las convenciones de nombre tienen como objetivo la correcta "Capitalización" a la hora de declarar los nombres de paquetes, clases, métodos, variables, constantes... Al utilizar la palabra "Capitalización" se intenta referir a la utilización de mayúsculas y minúsculas a la hora de crear un nombre identificador para los elementos anteriormente mencionados.
Existen distintos tipos de Capitalización y en este caso 4(3+1):
A.1. Pascal case:Se refiere a utilizar mayúsculas en la inicial de cada palabra y el resto de las letras tendrán que ser minúsculas sin excepción alguna.
public class MyClass {
}
A.2. Camel case:Se refiere a utilizar mayúsculas en la inicial de cada palabra con excepción de la primera letra de la primera palabra y el resto de las letras tendrán que se minúsculas.
public Color getColor
() { return color;
}
A.3. Upper case:Se refiere a utilizar mayúsculas en todo el conjunto.
final int MYCONSTANT = 2;
A.4. Lower case:Se refiere a utilizar minúsculas en todo el conjunto.
package mypackage;
Nota: Este ultimo fue agregado para efectos de este documento en especifico.
9. Convenciones de nombresLas convenciones de nombres hacen a los programas mas fáciles de leer. También dan información acerca de un elemento solo con leer su nombre lo que resulta muy útil a la hora de entender el código.
Elemento | Convención | Ejemplo |
Paquetes | El prefijo del nombre de los paquetes debe respetar la convención "Lower case" y debe ser uno de los nombres de dominio de alto nivel (com, edu, gov, net...). Los subsecuentes componentes del nombre del paquete vari'an de acuerdo a las convenciones de nombres internas de cada organización. | package com.sun.eng; package com.apple.quicktime.v2;
|
Clases e interfaces | Los nombres de las clases e interfaces deben ser sustantivos y respetar la convención "Pascal case". Evitar usar abreviaturas a no ser que la abreviatura sea mas conocida como URL o HTML | public class ImageSprite{} public interface RasterDelegate {}
|
Métodos | El nombre de los métodos deben ser vervos y respetar la convención "Camel case" | public void drawImage() {} public void fillPolygon() {}
|
Variables | El nombre de todas las variables debe respetar la convención "Camel case". Los nombres de variables no deben empezar con guion bajo o el signo dolar. Los nombres de variables deben ser cortos pero significativos, se deben evitar nombres de un solo carácter, excepto para variables de índices temporales. Nombres comunes para variables temporales son i, j, k, m, y n para enteros; c, d, y e para caracteres. | int i; char c;
|
Constantes | El nombre de todas las constantes debe respetar la convención "Upper case". En caso de existir mas de una palabra, estas serán separadas por un guion bajo. | final float PI = 3.14; final float WINDOW_SCALE = 2;
|
10. Hábitos de programación10.1. Acceso a variables de instancia y de clase:Por principios de encapsulación ninguna variable de instancia o clase a de ser publica sin una buena razón. Para acceder a variables de instancia o clase se utilizan los denominados métodos set o get.
10.2. Acceder a variables y métodos de claseEvitar usar un objeto para acceder a una variable o método de clase. Usar el nombre de la clase en su lugar.
MyClass.classMethod(); // Correcto
myObject.classMethod(); // Incorrecto
10.3. ConstantesLas constantes numéricas no se deben codificar directamente, excepto se trata de un bucle for.
10.4. Asignacion de variablesEvitar asignar el mismo valor a variables en la misma sentencia.
var1 = var2 = 5; // Evitar
10.5. Hábitos varios10.5.1. ParéntesisEs muy recomendado usar paréntesis en expresiones que implican distintos operadores aun cuando no sea necesario. Esto facilita la comprensión del código.
10.5.2. Valores de retornoEs apropiado hacer que la estructura del programa se ajuste a su intención. Por Ejemplo:
if (booleanExpression) {
return true;
} else {
return false;
}
En lugar de hacer:
return booleanExpression;
10.5.3. Expresión ternariaSi una expresión incluye un operador binario antes del signo ? en el operador ternario , se debe
colocar entre paréntesis. Ejemplo:
(myVar >= 0) ? 20 : -20;
10.5.4 Comentarios especialesUsar XXX en un comentario para indicar que algo tiene algún error pero funciona. Usar FIXME
para indicar que algo tiene algún error y no funciona.
Muchas gracias por leer, y espero que les sea de ayuda.