Artículos de Tecnología

Hacer que el código sea más sencillo con refactorización en Java

Alex Felipe
Alex Felipe
img-capa

Es muy común en nuestro día a día, como desarrolladores, lidiar con códigos escritos por otros desarrolladores, ya sea para comprender una regla de negocio o para la evolución del programa en general.

Para comprender mejor esta situación, consideremos una aplicación Java para un inventario de productos. En esta aplicación, tenemos el siguiente código:

public class ProductoDAO { 
    public List<Producto> lista(){ 
    // Implementación cualquiera que devuelva una lista de productos 
    } 
    public void productosResumidos(){ 
        List<Producto> prs = lista(); 
        for (Producto p : prs) { 
            String dse = p.getDescripcion().trim(); 
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < dse.length(); i++) { 
                if(i > 15) { 
                    sb.append("...");
                    break;
                } 
                sb.append(dse.charAt(i));
            } 
            String da = sb.toString(); 
            String vf = p.getValor().toString().replace(".", ","); 
            String df = p.getId() + " - " + da + " - $ " + vf;  
            System.out.println(df); 
        } 
    } 
}

Tenga en cuenta que se trata de una clase DAO para productos. Para aclarar más el ejemplo, consideremos el siguiente modelo para la clase Producto:

public class Producto { 
    private Integer id; 
    private String descripcion; 
    private Double valor; 
    // Constructores, getters, setters y métodos
}

Cuando ejecutamos el método productosResumidos(), obtenemos el siguiente resultado:

 1 - Soda de ... - $ 5,0 
 2 - Bocadillo Doritos ... - $ 3,5 
 3 - Pan Blanco ... - $ 4,5 
 4 - Leche ... - $ 2,75 
 5 - Jugo ... - $ 4,0

Al observar el resultado, tenemos un resumen de los productos almacenados en el inventario. Sin embargo, al mirar rápidamente la implementación del método productosResumidos(), ¿es fácil comprender cómo se obtuvo este resultado? En principio, no...

Entendiendo la refactorización de código

En otras palabras, hemos llegado a una situación en la que tiene sentido detenerse y pensar en formas de mejorar nuestro código actual, ¿verdad?

Este proceso de mejora de código se conoce técnicamente como code refactoring (o refactorización de código).

Esta técnica, en general, consta de diversas prácticas que buscan mejorar el código en los siguientes aspectos:

En principio, puedes estar pensando:

"La refactorización es genial, pero si lo hago, ¿estaré modificando el comportamiento de mi aplicación?"

La primera impresión que tenemos durante este tipo de proceso es que nuestra aplicación será modificada y tendrá comportamientos diferentes.

Esto es bastante común, pero la idea de la refactorización es aplicar técnicas que solo modifiquen el aspecto visual del código, es decir, el comportamiento inicial sigue siendo el mismo.

Puede parecer demasiado bueno para ser verdad, ¿verdad? Entonces, apliquemos algunas técnicas de refactorización al código que vimos inicialmente y veremos la magia en acción.

Renombrar variables

La primera de las técnicas que aplicaremos será cambiar los nombres de las variables. Al principio puede parecer trivial, pero echemos un vistazo a un fragmento de nuestro código:

List<Producto> prs = lista(); 
for (Producto p : prs) { 
    String dse = p.getDescripcion().trim();
    // resto del código 
}

Para ti, ¿psr, p, dse tienen algún significado? Para mí y para cualquier persona que vea este código por primera vez, no tienen ningún significado a primera vista. Entonces, ¿qué tal si hacemos la siguiente modificación?

List<Producto> productos = lista(); 
for (Producto producto : productos) { 
    String descripcionSinEspacios = producto.getDescripcion().trim();
    // resto del código
}

Ahora queda claro que estamos tratando con una lista de productos, y para cada producto de la lista de productos estamos obteniendo una descripcionSinEspacios.

¡Observa que incluso es más fácil de leer! En otras palabras, durante el proceso de refactorización, renombrar variables facilita la lectura del código. Por tanto, cuanto más significativo sea el nombre de la variable, más fácil será su lectura. Ajustemos los otros puntos:

public void productosResumidos(){ 
    List<Producto> productos = lista();
    for (producto producto : productos) {
        String descripcionSinEspacios = producto.getDescripcion().trim();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < descripcionSinEspacios.length(); i++) {
            if(i > 15) { sb.append("...");
            break;
        } 
        sb.append(descripcionSinEspacios.charAt(i));
    } 
    String descripcionAjustada = sb.toString();
    String valorFormateado = producto.getValor().toString().replace(".", ","); 
    String descripcionFinal = producto.getId() + " - " + descripcionAjustada + " - $ " + valorFormateado; 
        System.out.println(descripcionFinal); 
    } 
}

Tenga en cuenta que la variable sb aún se mantiene en referencia a la instancia de la clase StringBuilder. Dado que es una instancia de una clase comprensible para la mayoría de desarrolladores y no tiene un nombre muy corto, no hay problema en mantenerla como sb.

En otras palabras, para casos en los que estamos creando una instancia de una clase con un nombre largo, podemos usar una abreviatura. En este caso, con el StringBuilder, podríamos incluso usar builder, que tendría suficiente significado.

Recuerda: durante este proceso de renombrar variables, lo más importante es que al leer el código, la lectura sea fácil y directa. Es decir, incluso para alguien que no conozca la implementación en profundidad, debería ser comprensible solo leyendo el código.

Extracción de métodos

Aunque nuestro código ha mejorado significativamente en términos de legibilidad, es claro, con nombres más claros para los seres humanos, pero es bastante complejo. Si observamos, tenemos muchas líneas dentro de un solo método.

Dado esta situación, ¿qué podemos hacer para solucionar este problema? Para este tipo de escenarios, podemos aplicar la conocida técnica como extracción de métodos. Ahora puedes estar pensando:

"Está bien, pero ¿cómo funciona esta técnica?"

Básicamente, durante la lectura del código, realizamos un análisis y vemos qué hace un conjunto de código, como este fragmento:

String descripcionSinEspacios = producto.getDescripcion().trim();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < descripcionSinEspacios.length(); i++) {
    if(i > 15) { 
        sb.append("...");
        break; 
    } 
    sb.append(descripcionSinEspacios.charAt(i));
} 
String descripcionAjustada = sb.toString();

Si observamos con atención, este código tiene como objetivo ajustar una descripción de un producto, ¿verdad? Entonces, ¿qué tal si convertimos todo este código en este método aquí?

private String ajustarDescripcion(Producto producto) {
    String descripcionSinEspacios = producto.getDescripcion().trim();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < descripcionSinEspacios.length(); i++) {
        if(i > 15) { 
            sb.append("...");
            break;
        } 
        sb.append(descripcionSinEspacios.charAt(i));
    } 
    return sb.toString(); 
}

Entonces, solo necesitamos realizar la siguiente llamada dentro de nuestro código anterior:

public void productosResumidos(){ 
    List<Producto> productos = lista(); 
    for (Producto producto : productos) {
        String descripcionAjustada = ajustarDescripcion(producto);
        String valorFormateado = producto.getValor().toString().replace(".", ","); 
        String descripcionFinal = producto.getId() + " - " + descripcionAjustada + " - $ " + valorFormateado; 
        System.out.println(descripcionFinal); 
    } 
}

¡Mira cómo nuestro método se ha reducido! Es aún más fácil de leer de esta manera y podemos hacer lo mismo con otros puntos del código. Por ejemplo, este:

String valorFormateado = producto.getValor().toString().replace(".", ",");

Básicamente, el código a la derecha formatea el valor de acuerdo con la moneda, ¿verdad? En este caso, nada nos impide extraer un método aquí también. ¿Qué tal si lo dejamos de esta manera?

private String formatearMoneda(Producto producto) { 
    return producto.getValor().toString() .replace(".", ","); 
}

Aprovechando, también podemos aplicar el mismo concepto al fragmento de código siguiente:

String descripcionFinal = producto.getId() + " - " + descripcionAjustada + " - $ " + valorFormateado;

Vamos a extraerlo a:

private String resumirLaDescripcion(Producto producto, String descripcionAjustada, String valorFormateado) { 
    return producto.getId() + " - " + descripcionAjustada + " - $ " + valorFormateado;
}

Ahora, veamos cómo se ve ese método inicial:

public void productosResumidos(){ 
    List<Producto> productos = lista();
    for (Producto producto : productos) {
        String descripcionAjustada = ajustarDescripcion(producto);
        String valorFormateado = formatearMoneda(producto);
        String descripcionFinal = resumirLaDescripcion(producto, descripcionAjustada, valorFormateado); 
        System.out.println(descripcionFinal);
    } 
}

Mucho más fácil de leer, ¿verdad? Incluso podemos simplificarlo más. En lugar de enviar 3 parámetros para crear la descripción final, podemos enviar solo el producto y llamar a los otros métodos dentro del método resumirLaDescripcion():

public void productosResumidos(){ 
    List<Producto> productos = lista();
    for (Producto producto : productos) {
        String descripcionFinal = resumirLaDescripcion(producto);                    
        System.out.println(DescripcionFinal); 
    } 
} 
private String resumirLaDescripcion(Producto producto) { 
    String descripcionAjustada = ajustarDescripcion(producto);
    String valorFormateado = formatearMoneda(producto);
    return producto.getId() + " - " + descripcionAjustada + " - $ " + valorFormateado; 
}

Observa cuánto ha cambiado nuestro código:

public void productosResumidos(){ 
    List<Producto> productos = lista(); 
    for (Producto producto : productos) { 
        String descripcionFinal = resumirLaDescripcion(producto); 
        System.out.println(descripcionFinal); 
    } 
} 
private String resumirLaDescripcion(Producto producto) { 
    String descripcionAjustada = ajustarDescripcion(producto);
    String valorFormateado = formatearMoneda(producto);
    return producto.getId() + " - " + descripcionAjustada + " - $ " + valorFormateado;
} 
private String formatearMoneda(Producto producto) {
    return producto.getValor().toString() .replace(".", ",");
} 
private String ajustarDescripcion(Producto producto) {
    String descripcionSinEspacios = producto.getDescripcion().trim();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < DescripcionSinEspacios.length(); i++) {
        if(i > 15) { 
            sb.append("...");
            break;
        } 
        sb.append(DescripcionSinEspacios.charAt(i));
    } 
    return sb.toString();
    String da = sb.toString();
    String vf = p.getValor().toString().replace(".", ",");
    String df = p.getId() + " - " + da + " - $ " + vf; System.out.println(df);
}

Ahora, nuestro método productosResumidos() es mucho más fácil de entender. Sabemos inmediatamente que utiliza cada producto, obtenemos una Descripción final y la imprimimos.

Conclusión

Tenga en cuenta que, aunque el código parece complejo, al aplicar algunas técnicas de refactorización, hemos podido mejorar significativamente el aspecto de lectura y comprensión de nuestro código. En este caso, vimos la técnica de extracción de métodos y el cambio de nombres de variables a nombres más amigables. Si te gusta el tema, te animamos a profundizar en [Formación en Java Orientada a Objetos](Cursos Java Orientados a Objetos | Formación | Alura (aluracursos.com) ), que tenemos en Alura Latam!

img-autor

Alex Felipe

Instructor y desarrollador con experiencia en Java, Kotlin, Android. Creador de más de 40 cursos, como Kotlin, Flutter, Android, persistencia de datos, comunicación con Web API, personalización de pantalla, pruebas automatizadas, arquitectura de Apps y Firebase. Es experto en Programación Orientada a Objetos, siempre con el objetivo de compartir las mejores prácticas y tendencias en el mercado de desarrollo de software. Trabajó durante 2 años como editor de contenidos en el blog de Alura y hoy todavía escribe artículos técnicos.

Artículo traducido para Alura Latam por Brenda Souza

Artículos de Tecnología

En Alura encontrarás variados cursos sobre . ¡Comienza ahora!

Precios en:
USD
  • USD
  • BOB
  • CLP
  • COP
  • USD
  • PEN
  • MXN
  • UYU

Semestral

  • 273 cursos

    Cursos de Programación, Front End, Data Science, Innovación y Gestión.

  • Videos y actividades 100% en Español
  • Certificado de participación
  • Estudia las 24 horas, los 7 días de la semana
  • Foro y comunidad exclusiva para resolver tus dudas
  • Luri, la inteligencia artificial de Alura

    Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana

  • Acceso a todo el contenido de la plataforma por 6 meses
US$ 65.90
un solo pago de US$ 65.90
¡QUIERO EMPEZAR A ESTUDIAR!

Paga en moneda local en los siguientes países

Anual

  • 273 cursos

    Cursos de Programación, Front End, Data Science, Innovación y Gestión.

  • Videos y actividades 100% en Español
  • Certificado de participación
  • Estudia las 24 horas, los 7 días de la semana
  • Foro y comunidad exclusiva para resolver tus dudas
  • Luri, la inteligencia artificial de Alura

    Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana

  • Acceso a todo el contenido de la plataforma por 12 meses
US$ 99.90
un solo pago de US$ 99.90
¡QUIERO EMPEZAR A ESTUDIAR!

Paga en moneda local en los siguientes países

Acceso a todos
los cursos

Estudia las 24 horas,
dónde y cuándo quieras

Nuevos cursos
cada semana