Refactorizacion
El término refactorizar dentro del campo de la Ingeniería del Software hace referencia a la modificación del código sin cambiar su funcionamiento. Se emplea para crear un código más claro y sencillo, facilitando la posterior lectura o revision de un programa. Se podría entender como el mantenimiento del código, para facilitar su comprensión, pero sin añadir ni eliminar funcionalidades. Refactorizar código consiste en crear un código más limpio.
La refactorización debe ser un paso aislado en el diseño de un programa, para evitar introducir errores de código al reescribir o modificar algunas partes del mismo. Si después de refactorizar hemos alterado el funcionamiento del código, hemos cometido errores al refactorizar.
Se refactoriza para:
Limpieza del código, mejorando la consistencia y la claridad.
Mantenimiento del código, sin corregir errores ni añadir funcionalidades.
Elimina el código “muerto”, y se modulariza.
Facilita el futuro mantenimiento y modificación del código.
Los siguientes apartados están inevitablemente relacionados entre si, ya que todas las técnicas o reglas persiguen el mismo fin.
Convenciones de escritura de Java
Las convenciones de código existen debido a que la mayoría del coste del código de un programa se usa en su mantenimiento, (casi ningún programa se mantiene toda su vida con el código original), y mejoran la lectura del código permitiendo entender código nuevo mucho más rápido y a fondo. En la web de Sun Microsystems se recogen dichas convenciones en una guía en ingles, o una traducción al castellano cortesía de javaHispano.
Por su parte Google también ha creado recientemente una Guía de estilo para Java.
Para que las convenciones funcionen, cada programador debe tratar de ser lo más fiel posible a estas.
Ficheros
Todos los ficheros fuente de java son ficheros de texto plano cuyo nombre termina con la extensión .java
Dentro de cada fichero .java tenemos 4 partes en el siguiente orden:
Posibles comentarios sobre la clase (autor, fecha, licencias, etc)
Sentencia package. Toda clase debe estar en un paquete.
Sentencias import. Importar cada clase en una linea separada.
La definición de una única clase o interface cuyo nombre es idéntico al nombre del fichero sin la extensión.
/* Correcto */ /* Incorrecto */
import java.awt.Frame; import java.awt.*;
import java.awt.Graphics;
Posteriormente dentro de la definición de la clase, aplicamos el siguiente orden:
Sentencia class o interface
Variables de clase (static)
Variables de instancia (Atributos de la clase)
Constructores (Si hay sobrecarga deben ir seguidos)
Métodos (Si hay sobrecarga deben ir seguidos)
Declaraciones de variables
int edad;
int cantidad;
Las variables locales se deben inicializar en el momento de declararlas o justo después. Se declaran justo antes de su uso, para reducir su ámbito.
Las variables de instancia o de clase se declaran al comienzo de la definición de la clase.
Los arrays se pueden inicializar en bloque:
int[] array =
{
0, 1, 2, 3
};
// ó
int[] array = { 0, 1, 2, 3 };
String[] nombres; //correcto
String nombres[]; //incorrecto
Nombres de identificadores
Para los identificadores podemos usar las letras anglosajonas y números de la tabla ASCII. No se debe usar caracteres con tilde ni la (ñ). Las barras bajas o guiones tampoco se usan.
Los nombres de los identificadores deben ser siempre lo más descriptivos posible, ya sea variable, método o clase. Solo se usan identificadores de un solo carácter para representar los contadores del bucle for, y comienzan en la letra i.
Nombre de Package: siempre en minúsculas.
Nombre de las clases o interfaces: UpperCamelCase.
Nombre de los métodos: lowerCamelCase. Suelen ser verbos o frases.
Nombres de constantes: CONSTANT_CASE. Todo el mayúsculas, separando con barra baja.
Variables locales, atributos de la clase, nombres de parámetros: lowerCamelCase.
Magic Numbers
Se conoce bajo este nombre a cualquier valor literal (“texto” o numérico) empleado en el código sin ninguna explicación. Se deben sustituir siempre que se pueda por una constante que identifique su finalidad.
//incorrecto
int precioConIva = precioBase + (0.21 * precioBase);
//correcto
//Se define en la clase
final static double IVA = 0.21;
//Se utiliza en un método
int precioConIva = precioBase + (IVA * precioBase);
Estructura del código
Debemos usar la codificación UTF-8
En las sentencias de control de flujo (if, else, for, do-while, try-catch-finally) se incluyen llaves { }, incluso si no contienen código o es una sola instrucción. Se alinean las llaves {} al inicio de línea.
if (final < indice) {
filaInicial = indice – numeroFilas;
} else if (indice < filaInicial) {
filaInicial = indice;
}
Una sola instrucción por linea.
Las líneas de código no deben superar los 100 caracteres. Si no, se deben romper antes de algún operador.
Si la declaración del método es demasiado larga, o una expresión aritmética es demasiado larga, o en una sentencia if, debo romper.
Si una operación aritmética o lógica se compone de distintos tipos de operaciones con distinta jerarquía, se deben usar paréntesis para facilitar su legibilidad.
public void ejecutarAccion(
TipoParametro parametro1, TipoParametro parametro2, TipoParametro parametro3){
...
}
if ((condicion1 && condicion2)
|| (condicion3 && condicion4)
|| !(condicion5 && condicion6)) {
llamarMetodo();
}
longName1 = longName2 * (longName3 + longName4 - longName5)
+ (4 * longname6); //Siempre con el operador al principio de línea
Los espacios en blanco mejoran la legibilidad. Se deben colocar entre operadores, después de los puntos y coma de los bucles for, después de los operadores de asignación, etc.
cantidadTotal = cantidadInicial + cantidadFinal;
for(int i = 0; i < cantidadTotal; i++){
...
}
public String getItem(int fila, int columna) {
...
}
getItem(cantidadInicial, cantidadFinal);
Debemos estar familiarizados y poner en práctica las convenciones recogidas en alguna de las guías de estilo indicadas.
Bad Smells
Se conoce como Bad Smell o Code Smell (mal olor) 1) a algunos indicadores o sintomas del código que posiblemente ocultan un problema más profundo.
Los bad smells no son errores de código (bugs), ya que no impiden que el programa funcione correctamente, pero son indicadores de fallos en el diseño del código que dificultan el posterior mantenimiento del mismo y aumentan el riesgo de errores futuros.
Algunos de estos síntomas son:
Métodos muy largos (Long Method). Los método de muchas lineas dificultan su comprensión. Un método largo probablemente está realizando distintas tareas, que se podrían dividir en otros métodos. Las funciones deben ser los más pequeñas posibles (3 lineas mejor que 15). Cuanto más corto es un método, más fácil es reutilizarlo. Un método debe hacer solo una cosa, hacerla bien, y que sea la única que haga.
Clases muy grandes (Large class). Problema anterior aplicado a una clase. Una clase debe tener solo una finalidad. Si una clase se usa para distintos problemas tendremos clases con demasiados métodos, atributos e incluso instancias. Las clases deben el menor numero de responsabilidades y que esté bien delimitado.
Lista de parámetros extensa (Long parameter list). Las funciones (o métodos en Java) deben tener el mínimo número de parámetros posible, siendo 0 lo perfecto. Si un método requiere muchos parámetros puede que sea necesario crear una clase con esa cantidad de datos y pasarle un objeto de la clase como parámetro. Del mismo modo ocurre con el valor de retorno, si necesito devolver más de un dato.
En la siguiente página web tenemos la mayoría de Bad Smells agrupados en 5 tipos.
Buenas prácticas
//instanciación lenta
String lenta = new String("objeto string");
//instanciación rápida
String rapida = "objeto string";
Tipos primitivos mejor que clases wrapper (envoltorio) : Las clases wrapper al ser objetos, proveen de métodos para trabajar mejor con ellas, pero al igual que los Strings, son más lentos que los tipos primitivos.
int x = 10;
int y = 10;
Integer x1 = new Integer(10);
Integer y1 = new Integer(10);
String x2="hola";
String y2 = new String("hola");
System.out.println(x == y); //TRUE
System.out.println(x1 == y1); //FALSE, ya que son 2 objetos distintos
System.out.println(x2 == y2); //FALSE, ya que son 2 objetos distintos
...
int resultadoTotal = resultadoInicial - resultadoFinal;
...
Bucle for. Optar por el for siempre que se pueda (frente a while, do-while). Las ventajas son que reune todo el control del bucle en la misma linea (inicio, fin, e incremento), y la variable de control ('i') no es accesible desde fuera de él. Si se necesita modificar su variable de control, usar otro bucle.
Constantes: Cualquier valor literal debe ser definido como constante, excepto 1, -1, 0 ó 2 que son usados por el bucle for.
Refactorización de Eclipse
Eclipse tiene distintos métodos de refactorización. Dependiendo de sobre qué mostremos el menú de refactorización, nos ofrecerá unas u otras opciones. Para refactorizar pulsaremos click derecho sobre el nombre del elemento deseado, y deplegaremos la opción Refactor del menu contextual.
Los métodos de refactorización, también llamados patrones de refactorización, nos permiten plantear casos y previsualizar las posibles soluciones que se nos ofrecen. Podemos seleccionar diferentes elementos para mostrar su menú de refactorización ( una clase, una variable, método, bloque de instrucciones, expresion, etc ). A continuación se muestras algunos de los métodos más comunes:
Rename: Es la opción empleada para cambiar el identificador a cualquier elemento (nombre de variable, clase, método, paquete, directorio, etc). Cuando lo aplicamos, se cambian todas las veces que aparece dicho identificador.
Extract Constant: Convierte un número o cadena literal en una constante. Se puede ver donde se realizarán los cambios, y también el estado antes y después de refactorizar. Después, todas las apariciones de esa cadena se sustituyen por el nombre de la constante. Esto se utiliza modificar el valor en un solo lugar.
Extract Local Variable: Convierte un número o cadena literal en una variable de ámbito local. Si esa misma cadena de texto existe fuera del bloque o del método, no se aplica el cambio. Parecido al patrón anterior, pero para aplicar dentro de método o bloques de código entre llaves { }.
Extract Method: Convierte un bloque de código en un método, a partir de un bloque cerrado por llaves { }. Eclipse ajusta las parámetros y el retorno del método. Es muy útil cuando detectamos bad smells en métodos muy largos, o en bloques de código que se repiten.
File fichero = new File("datos.dat");
PrintWriter escritor;
escritor = new PrintWriter(fichero);
PrintWriter escritor = new PrintWriter(new File("datos.dat"));
Extract Interface: Este patrón de refactorización nos permite seleccionar los métodos de una clase para crear una Interface. Una Interface es una plantilla que define los métodos acerca de lo que puede hacer una clase. Define los métodos de una clase (Nombre, parámetros y tipo de retorno) pero no los desarrolla.
Eclipse también nos permite ver un histórico de la refactorización que se ha hecho en un proyecto, abriendo el menu Refactor → History.
Prácticas
Refactorización de código Java (Prueba práctica)
© 2026 Fernando Valdeón