lunes, 25 de agosto de 2008

Aprendiendo de un refactoring

He comenzado a reescribir algún código procedente de un proyecto anterior para el nuevo que estoy emprendiendo. En concreto un helper para genera tablas de administración de registros más flexibles y configurables que las generadas por Bake.

Cuando tu propio código ha envejecido unos meses puede llegar a hacerse bastante complicado de leer. De hecho, para ser sinceros, no había por dónde cogerlo. En otras palabras, no sabía por dónde empezar. Recordaba vagamente no estar muy satisfecho con la forma en que había escrito ese helper ya que, aunque funcionaba bien, no me atrevía normalmente a tocarlo mucho por si acaso.

Entonces se me ocurrió que ya que no veía como mejorar aquello, por lo menos intentaría hacerlo más legible. En cuanto empecé a conseguir legibilidad me fue fácil darme cuenta de dónde estaba metiendo la pata (en casi todo, pero esa es otra historia).

Volver pronto a casa

Por ejemplo, tengo (tenía) mucha tendencia a escribir código así:

if ($hay_que_procesar_esto == true) {
procesar...
procesar...
procesar
} else {
return false;
}
return $resultado_del_proceso;


Lo cual en su momento me parecía de lo más elegante y tal y cual. Sin embargo, tras leer este artículo de Felix Geisendörfer, caí en la cuenta de que la solución "Vuelve pronto a casa" es bastante más legible;

if (!$hay_que_procesar_esto) {
return false;
}

procesar...
procesar...
procesar...

return $resultado_del_proceso;


La lógica que apoya esta forma de organizar el código es muy convincente. Si escribes una función o un método, primero intenta lidiar con las excepciones y regresa. Deja para el final el algoritmo general, que podrá ejecutarse sin problemas y será más fácil de depurar llegado el caso.

Normaliza los datos

En mi caso, el helper podría lidiar con un array de registros resultado de una búsqueda con un model::find(). Pero ¿qué ocurre si quiero usar registros relacionados? La estructura del array es ligeramente distinta ¿no? Y solo requiere un sencillo bucle pasar de una a otra.

Pues nada, lo lógico (yo no lo hacía en el método que estaba reescribiendo) es chequear si los datos se ajustan a una estructura dada y, si no es así, normalizarlos antes de empezar.

De otro modo, lo que consigues es tener que crear un algoritmo o un proceso distinto para cada tipo de estructura posible y, si bien puede haber casos en que sea necesario hacerlo así, lo más seguro es que la modificación sea fácil.

No te repitas

Una de las cosas que más me llamó la atención fue comprobar que mi código hacía un montón de tareas duplicadas. A veces con los mismos datos de forma sucesiva (lo que supone el doble de tiempo de ejecución). Eso no era evidente en la primera versión, pero al reescribir esas situaciones empezaron a manifestarse con claridad.

La repetición de código no se refiere solo a ejemplos tan burdos como el mío, sino incluso al problema más sutil de los métodos que son muy similares entre sí y que tal vez sólo se diferencian en el ámbito de los datos que recuperan. Por ejemplo, un método index en un controlador que devuelve todos los registros existentes y otro método populares que devuelve todos los registros que tienen un mayor número de peticiones. La única diferencia entre ambos métodos sería una condición de búsqueda.

Como seguramente sabrás, el principio DRY (Don't Repeat Yourself) aboga por un código en el que se eviten al máximo las repeticiones. En el artículo de Nate Abele Art, MVC and CakePHP puedes encontrar un estudio muy interesante en el que se explica la aplicación de este principio. Es un clásico.

2 comentarios:

José Zanni dijo...

Uf... cada vez que retomo un proyecto mio "antiguo" (de varios meses, sin ir mas lejos) pasa esto.

Y ahora peor, porque estoy empezando con la version 1.2 y todos mis proyectos anteriores estan con la 1.1... :D

Por ahora ahi se quedan

José Zanni dijo...

Ha, me gustó ese sistema de "vuelve pronto a casa" :D