miércoles, 5 de mayo de 2010

Desconfíen de los parámetros booleanos

Este post es el primero de una serie que vamos a hacer con consejos sobre calidad del código y en particular sobre mantenibilidad (algo que consideramos muy importante).

Uno de las primeros consejos de legibilidad de código que uno recibe cuando empieza a programar profesionalmente es que no se deben usar "números mágicos" y que todos los valores literales con significado especial para nuestro programa deben ser reemplazados por constantes. Esto se hace para protegernos de posibles cambios pero sobre todo para mejorar la legibilidad de nuestro código (después de todo se suele usar la constante PI, cuyo valor es dificil que cambie).

Sin embargo, este criterio no se sigue en el caso de parámetros booleanos y entonces es común ver métodos con definiciones como esta:

public FileWriter(File file, boolean append) {
 
}
 

Este es un constructor que es parte de la biblioteca standard de Java. Si se mira el código del lado del método la cosa no parece tan fea. Si uno elige un buen nombre para el parámetro el código queda claro:

public FileWriter(File file, boolean append) {
    if( ! append) {
            //trunca el archivo
    }
}
Sin embargo, si uno mira el código en la llamada al método el resultado es bastante críptico:
FileWriter writer = new FileWriter(file,true);
¿Qué significa ese true? ¿Significa "truncá el archivo" o significa "hace append al archivo"?. Esto depende del nombre que le hayamos puesto a la variable y la única manera de saberlo es ir a la definición del método y leer el código. Es exactamente lo mismo que nos pasa cuando nos encontramos un "número mágico".
Es interesante ver como este efecto no se da en todos los lenguajes. En aquellos que tienen la posibilidad de parámetros con nombre como Smalltalk (o permiten simularlos como Ruby) el código de la llamada es completamente legible:
writer = FileWriter.new(file,:append => true);
¿Como podemos hacer para solucionar este problema? Lo primero que se nos ocurre es hacer lo mismo que normalmente hacemos con los literales de otros tipos, es decir reemplazarlos por constantes con nombres significativos:
public enum {
  Append,
  Truncate
} CreationMode;
 
public FileWriter(File file, CreationMode creationMode) {
    if(creationMode == CreationMode.Truncate) {
            //trunca el archivo
    }
}
 
FileWriter writer = new FileWriter(file,CreationMode.Append);

Sin embargo, antes de tomar este camino es bueno revisar si la existencia de este parámetro booleano no nos está alertando sobre un problema más de fondo. Básicamente este tipo parámetros permite al código llamador controlar el flujo de ejecución del método llamado, algo sospechoso no ya desde el punto de vista de la programación orientada a objetos si no desde la óptica de la programación estructurada. Por lo tanto es probable que si revisamos el código del método nos encontremos con que en realidad la mejor solución es tener dos métodos cada uno con uno de los comportamientos que originalmente ocurrían al pasar el flag en true o false.

Como normalmente ocurre con estos consejos, esto no significa que no haya que usar jamás parámetros booleanos, si no que es bueno antes de usarlos considerar otras opciones. Además tenemos que tener en cuenta que cuando escribimos un método no solo importa la calidad del código del método que escribimos si no que su interfaz no lleve a los usuarios de dicho método a escribir mal código (sobre todo sabiendo que muchas veces los usuarios vamos a ser nosotros mismos o quizás un psicopata violento que sabe donde vivimos)

No hay comentarios:

Publicar un comentario