Refactoring en Español (4)

Hace ya un par de semanas que publiqué la última entrega de “Refactoring en Español”. Voy a seguir con el ejemplo de Fowler en el libro “Refactoring” por el punto donde lo dejamos.

Removing Temps

Antes de comenzar hacemos el siguiente refactor “sintáctico”.

Reordenamos en Customer.statement para poner juntas estas lineas y poder cambiar
lo siguiente:

  Enumeration<Rental> rentals = _rentals.elements();
  while (rentals.hasMoreElements()) {
   Rental each = (Rental) rentals.nextElement();
   ...
  }

por:

  for (Rental each: _rentals ) {
    ...
  }

TESTS. Sí, aunque sea un cambio trivial, refactorizar consiste en hacer pequeños cambios pero siempre muy seguros; por eso lanzamos las pruebas unitarias cada vez que realizamos un cambio en nuestro código.

A partir de aquí podemos ver más claramente la necesidad/posibilidad de
trabajar sobre las variables totalAmount y frequentRenterPoints.

Primero trabajamos con totalAmount y creamos el método getTotalCharge como sigue:

 /**
  * Rolls over the list of rentals calculating
  * the total amount to be charged.
  * 
  *  
  */
 private double getTotalCharge() {
  double totalAmount = 0;
  for (Rental each: _rentals ) {
   totalAmount += each.getCharge();
  }
  return totalAmount;
 }

Obsérvese que lo hacemos replicando el bucle que recorre la lista de Rentals y
moviendo el código que suma la cantidad correspondiente del bucle original al
del método (que se ejecuta al final del bucle original y fuera de éste).

Este cambio es conceptualmente difícil de concebir e implementar (este ejemplo es
un ejercicio de laboratorio y en la práctica nos podemos encontrar con casos mucho
más difíciles de ver).

El método statement queda como sigue después de este cambio:

 public String statement() {
  int frequentRenterPoints = 0;
  String result = "Rental Record for " + getName() + "\n";
  for (Rental each: _rentals ) {
   frequentRenterPoints += each.getFrequentRenterPoints();
   // show figures for this rental
   result += "\t" + each.getMovie().getTitle() + "\t"
     + String.valueOf(each.getCharge()) + "\n";
   totalAmount += each.getCharge();
  }
  // add footer lines
  result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
  result += "You earned " + String.valueOf(frequentRenterPoints)
    + " frequent renter points";
  return result;
 }

TESTS. En este caso está más justificado que nunca puesto que no se trata de un cambio nada trivial.

A continuación aplicamos la misma receta a frequentRenterPoints

El método statement queda finalmente como sigue:

 public String statement() {
  String result = "Rental Record for " + getName() + "\n";
  for (Rental each: _rentals ) {
   // show figures for this rental
   result += "\t" + each.getMovie().getTitle() + "\t"
     + String.valueOf(each.getCharge()) + "\n";
  }
  // add footer lines
  result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
  result += "You earned " + String.valueOf(getTotalFrequentRenterPoints())
    + " frequent renter points";
  return result;
 }

TESTS. No olvidemos ejecutarlos después de cada cambio.

Finalmente, incidir en el comentario que hace Fowler sobre el hecho de que, tras este
cambio, tenemos tres bucles que recorren tres veces la lista de Rentals. Esto lo
tendremos en cuenta cuando estemos mejorando el rendimiento, pero no ahora porque
lo que nos preocupa es mejorar el diseño (sin incumplir con los requisitos), y con
los cambios efectuados hemos mejorado el diseño y la legibilidad del código, lo
que nos llevará a reducir los errores y aumentará la reusabilidad. (El ejemplo de
Fowler es muy bueno: ahora podemos escribir un método htmlStatement que imprime
el recibo en formato HTML, pero reutilizando todo el código que no tiene que ver
con la estética del mismo).

Tagged: