martes, 11 de enero de 2011

Como y porqué de las excepciones

Las excepciones fueron uno de los grandes avances de los lenguajes de programación modernos (y somos concientes de que al llamar modernos a lenguajes como Java o C++ se nos están cayendo varias sotas...).

Antes, cuando programábamos en C, nuestro código tenía esta pinta (ejemplo tomado del brillante "The Pragmatic Programmer" ).


    retcode = OK;
    if (socket.read(name) != OK) {
      retcode = BAD_READ;
    }
    else {
      processName(name);
      if (socket.read(address) != OK) {
        retcode = BAD_READ;
      }
      else {
        processAddress(address);
        if (socket.read(telNo) != OK) {
          retcode = BAD_READ;
        }
        else {
          // etc, etc...
        }
      }
    }
    return retcode;

Mientras que con excepciones podemos hacer


    retcode = OK;
    try {
      socket.read(name);
      process(name);
      socket.read(address);
      processAddress(address);
      socket.read(telNo);
      // etc, etc...
    }
    catch (IOException e) {
      retcode = BAD_READ;
      Logger.log("Error reading individual: " + e.getMessage());
    }
    return retcode;

Con lo cual es claro que las excepciones nos ayudan a escribir mucho mejor código. Sin embargo hay muchas situaciones donde no son la mejor solución y deben ser evitadas. Veamos el siguiente ejemplo

 try {
  f = Float.parse(userInput);
 } catch(MalformedFloat e) {
  f = defaultValue;
 }

En este caso, estamos aprovechando que el metodo parse dispara una excepción cuando le pasamos valores incorrectos para continuar con el procesamiento en el handler de esta excepción. Y estamos usando excepciones para manejar un caso no excepcional: los usuarios suelen equivocarse en el input. El problema es que las excepciones disparan un salto de control bastante parecido a un goto (cuando se produce una excepción se salta del contexto actual hacia otro contexto, probablemente en otra clase y con varias ejecuciones de bloques finally entre medio). El código que depende de excepciones para el manejo de situaciones normales suele tener el mismo tipo de problemas que el código que usa gotos.

Una alternativa sería escribir el mismo código de la siguiente manera:

 if(Float.canParse(userInput)) {
  f = Float.parse(userInput);
 } catch(MalformedFloat e) {
  f = defaultValue;
 }

Para hacer esto dependemos de que la clase Float nos brinde la posibilidad de chequear el input antes de usarlo, lo que nos da un buen consejo para cuando diseñamos nuestras propias clases: siempre ofrecer maneras de verificar que no se deberían producir excepciones.

Por supuesto, si la operación que dispara la excepción es muy costosa o es destructiva, nos veremos forzados a usar la primera alternativa.

No hay comentarios:

Publicar un comentario