Gerardo Contijoch

Experiencias del día a día trabajando con .NET – ASP.NET, C#, ASP.NET MVC y demas…

La importancia del uso de las excepciones adecuadas

Posted by Gerardo Contijoch en enero 2, 2009

Si hay algo que me gusta, eso es lanzar excepciones. Ojo, no es que me guste provocarlas (aunque puedo ser muy dañino durante la etapa de testing… hehe) sino que lo que me gusta es pensar en como y cuando lanzarlas. Me encanta pensar y razonar sobre que excepción es la más adecuada para cada caso, si hay que utilizar una ya existente o hay que crear una nueva, que mensaje asociarle a la excepción, atraparlas y relanzarlas, etc. Es como un juego en donde se pasa una pelota (¿PelotaException? :P) y solo se la tiene que quedar su dueño. Y no siempre es un juego fácil, hay que saber atrapar y lanzar la pelota o esta puede terminar en cualquier lado.

Hace unos días me encontré con un post de Jared Parsons, en el que explicaba el (aparentemente) correcto uso de las excepciones NotSupportedException y NotImplementedException. Básicamente la diferencia principal entre estas dos excepciones es que la primera se lanza cuando una clase no implementa un miembro y es posible determinar si el mismo esta o no implementado (en otras palabras, se sabe de antemano que el miembro en cuestión no esta implementado); mientras que la segunda se lanza cuando el miembro no esta implementado por cualquier otro motivo. A mi no me convence mucho ese ‘cualquier otro motivo’ ya que el único caso, además del presentado anteriormente, en el que no se implementa un método es el caso de que el código no esté terminado y al decir ‘cualquier otro motivo’ parece que se está dando una excusa para lanzar esa excepción cuando queramos.

Sumado al caso anterior, tenemos las excepciones ArgumentException, ArgumentNullException y ArgumentOutOfRangeException, todas muy similares.

Veamos el siguiente código:

   1: public void AgregarCompraAOrden(Orden o, Producto p, int cantidad){
   2:     if(o == null || p == null){
   3:         throw new ArgumentNullException(o == null ? "o" : "p");
   4:     }
   5:
   6:     if(o.EstaCerrada){
   7:         throw new ArgumentException("La orden debe estar abierta para que se le puedan agregar productos.", "o");
   8:     }
   9:
  10:     if(cantidad <= 0){
  11:         throw new ArgumentOutOfRangeException("cantidad", "La cantidad debe ser mayor a 0.");
  12:     }
  13:
  14:     /*...*/
  15: }

Supongamos que en todos los casos anteriores solo lanzamos ArgumentException (debido a que hay un problema con uno de los argumentos o parámetros del método). Nos sería imposible determinar cual es el problema sin leer el mensaje. La diferencia entre estas tres excepciones es mínima, pero el uso correcto de las mismas facilita enormemente la depuración del código.

No siempre es sencillo determinar la excepción a lanzar. Lo ideal siempre es utilizar las excepciones que ya vienen en el framework, pero muchas veces la respuesta esta en crear una nueva excepción únicamente para una situación particular. Puede parecer mucho trabajo para muy poco, pero no lo es. Veamos otro ejemplo:

   1: public void AgregarCompraAOrden(Orden o, Producto p, int cantidad, int descuento){
   2:
   3:     /* Validaciones de producto y orden */
   4:
   5:     if(cantidad <= 0){
   6:         throw new ArgumentOutOfRangeException("cantidad", "La cantidad debe ser mayor a 0.");
   7:     }
   8:
   9:     if(descuento < 0 || descuento > 100){
  10:         throw new DescuentoInvalidoException("El descuento no puede ser negativo ni mayor al 100%");
  11:     }
  12:
  13:     /*...*/
  14: }

En este caso la validación del descuento no lanza ArgumentOutOfRangeException (que seria la excepción esperada) sino una excepción propia que sólo se utiliza para indicar valores de descuento inválidos y nada más. Una vez más, la diferencia es mínima, pero el significado de utilizar ArgumentOutOfRangeException o DescuentoInvalidoException es muy diferente y nuestra lógica puede llegar a procesar las ordenes de manera diferente dependiendo de que excepción se lance.

Cabe aclarar una cosa en el caso anterior. Se creó una excepción nueva porque un producto con 0 como cantidad implica que no se agrega un producto (lo cual no tiene sentido dentro del método AgregarCompraAOrden), pero un descuento inválido puede significar que no se aplican descuentos o que la orden no se procesa hasta que no pase una revisión o cualquier otra cosa. Si para nosotros un descuento inválido es lo mismo que una cantidad inválida (es decir, un error en un número que evita que se procese la orden) entonces habría que considerar usar ArgumentOutOfRangeException en ambos casos debido a que no se espera que los mismos se procesen de manera diferente. La idea de usar distintas excepciones es informar distintas cosas, pero si para nosotros cualquier error es lo mismo (rara vez debería ser así…), entonces habría que usar las mismas excepciones ya que no nos interesa la información extra que nos puedan proveer los distintos tipos.

¡Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: