En este artículo veremos de qué forma tan sencilla podemos incorporar en el diseño de nuestras clases la capacidad de que emitan Excepciones en tiempo de ejecución, propagándose así por la cadena de responsabilidad de nuestras aplicaciones para avisar de que una función o tarea no se ha podido ejecutar correctamente.
En una entrada anterior vimos como podemos atrapar y reaccionar antes las excepciones en tiempo de ejecución producidas por errores en nuestras aplicaciones; ya sea como consecuencia de fallos en la escritura de nuestro código o bien del procesado de datos procedentes de agentes externos (accesos a bases de datos, consumo de API, adquiridas mediante dispositivos externos, etc).
Sin embargo esa es sólo una parte de la ecuación, ya que nos falta por conocer en qué modo podemos dotar a nuestras propias clases de la capacidad de convertirse en emisores de dichas excepciones. Es decir, la forma desacoplada y OOP de que el código que consume instancias de nuestra clase pueda reaccionar ante un problema derivado del uso de dicha clase: separación de responsabilidades con funcionalidad compartimentada.
Pongamos por caso que diseñamos una clase que ha de adquirir datos obtenidos mediante la consulta de una API remota, y que dicha API impone límites en su uso. Sin embargo, la clase no tiene por qué (y no debe) de establecer un comportamiento de reacción determinado frente al error en el acceso sobre dicha API. Puede que en una aplicación determinada donde se haga uso de dicha clase se deba de reaccionar de una forma, mientras que en otra aplicación se opte por tomar otra serie de medidas.
¿Cómo lidiar con el problema entonces? La respuesta pasa por ser nuestra clase quien emita una excepción, de modo que pueda ser atrapada y por tanto procesada por el fragmento de código que hace uso de dicha clase. Una ventaja añadida de que esto sea así es que, de este modo, podremos proporcionar en la excepción emitida datos específicos a la tarea de nuestra clase tales como un mensaje de error, descripción, número de error, etc.
Implementar la emisión de excepciones
Por supuesto, el modo en el que se implementan y emiten las excepciones no es exclusivo de las clases. De hecho podemos reproducir el mismo comportamiento en cualquier elemento del proyecto que permita llevar asociado código: Eventos, Manejadores de Menú y también los métodos o las propiedades calculadas, por ejemplo. Sin embargo, tienen más sentido cuando están ligadas al diseño de una clase en particular.
Las reglas sobre cuándo debe de implementarse este tipo de funcionalidad son las mismas que en aquellos casos en los que podríamos utilizar el bloque Try… Catch
; es decir, cuando no sea seguro que un proceso determinado cuente en tiempo de ejecución con todos los recursos necesarios o bien estos proporcionen la salida de datos esperada.
Todo lo que necesitamos es utilizar un fragmento de código similar al siguiente:
Dim error as New RuntimeException
error.message = "Se ha producido un error inesperado"
error.reason = "El formato devuelto por el servicio consultado no es adecuado"
error.ErrorNumber = 101
Raise error
Propiedades de la Excepción
El texto proporcionado en la propiedad message
será el que podamos mostrar posteriormente al usuario final mediante un MsgBox
o función equivalente cuando atrapemos la excepción; con el inconveniente de que está disponible bajo despliegue iOS.
La propiedad que sí está disponible para todas las pltaformas es reason
, y aunque puedes utilizarlo para mostrarlo al usuario final, se supone que no es su finalidad. Como puedes ver, también puedes indicar un número entero de error en la propiedad ErrorNumber
y que resultará especialmente útil para reaccionar ante diferentes tipos de excepciones mediante un bloque de tipo Select Case
.
Por otra parte, en este ejemplo el fragmento de código se encarga de crear una instancia a partir de la clase `RuntimeException`, si bien podría haber estado basada en cualquier otra de entre las disponibles que consideremos.
Propagación de la Excepción
La instrucción encargada de emitir la excepción propiamente dicha es:
Raise error
Y el modo en el que lo hace es de la siguiente forma:
- El método, Evento o Manejador de Menú desde el que se ha invocado la funcionalidad emisora de la excepción.
- El control que ha llamado al método responsable de invocar la funcionalidad emisora de la excepción.
- El último responsable es el objeto App siempre y cuando hayamos implementado el Manejador de Evento `UnhandledException`. En este caso, la aplicación continuará con su funcionamiento siempre que devolvamos el valor True como resultado; si se sale del evento devolviendo False (por omisión) entonces se finalizará con la ejecución del programa.
Con este sencillo mecanismo podemos (y deberíamos) dotar a nuestras clases de la capacidad de avisar a los consumidores de las clases de los problemas derivados de su función. Después de todo, el modo de reaccionar ante dichos problemas no debería de formar parte de la propia clase sino de la lógica de la aplicación que haga uso de la clase.
Esta entrada ha sido redactada con Snippery utilizando el lenguaje Markdown, y exportada como HTML para su uso en este sitio web.