c# - try - tipos específicos de excepciones más comunes en programacion
¿Escribes excepciones para problemas específicos o excepciones generales? (11)
@ Chris.Lively
Usted sabe que puede pasar un mensaje en su excepción, o incluso en los "códigos de estado". Estás reinventando la rueda aquí.
Tengo un código que da una identificación de usuario a una utilidad que luego envía un correo electrónico a ese usuario.
emailUtil.sendEmail(userId, "foo");
public void sendEmail(String userId, String message) throws MailException {
/* ... logic that could throw a MailException */
}
MailException
podría ser lanzado por una serie de razones, problemas con la dirección de correo electrónico, problemas con la plantilla de correo, etc.
Mi pregunta es: ¿creas un nuevo tipo de Excepción para cada una de estas excepciones y luego las tratas individualmente o creas una MailException y luego almacenas algo en la excepción (algo legible por computadora, no el texto descriptivo) que permite nosotros para hacer cosas diferentes en base a lo que realmente sucedió.
Editar: como aclaración, las excepciones no son para los registros y lo que no, esto se relaciona con la forma en que el código reacciona a ellos. Para continuar con el ejemplo del correo, digamos que cuando enviamos el correo puede fallar porque no tiene una dirección de correo electrónico, o porque no tiene una dirección de correo electrónico válida , o puede fallar ... etc.
Mi código querría reaccionar de manera diferente a cada uno de estos problemas (principalmente al cambiar el mensaje devuelto al cliente, pero también la lógica real).
¿Sería mejor tener una implementación de excepción para cada uno de estos problemas o una excepción general que tuviera algo interno (un enum decir) que permitiera al código distinguir qué clase de problema era?
Depende de lo que está haciendo tu aplicación. Es posible que desee arrojar excepciones individuales en casos como
- La aplicación es de alta disponibilidad
- Enviar un correo electrónico es particularmente importante
- El alcance de la aplicación es pequeño y el envío de correo electrónico es una gran parte de él
- La aplicación se implementará en un sitio que es remoto y solo obtendrá registros para la depuración
- Puede recuperar de un subconjunto de las excepciones encapsuladas en la excepción mailException pero no otras
En la mayoría de los casos, diría que solo ingrese el texto de la excepción y no pierda su tiempo al detallar las excepciones ya bastante granulares.
Si bien puede diferenciar la ejecución del código buscando la excepción, no importa si lo hace el "modo de jerarquía de excepción de catch" o "if (...) else ... modo de código de excepción"
pero si está desarrollando un software que va a ser utilizado por otras personas, como una biblioteca, creo que es útil, cree sus propios tipos de excepciones para advertir a las otras personas que su software puede arrojar otras excepciones que las normales, y que es mejor atrapar y resolverlos.
Cuando uso una biblioteca y sus métodos, simplemente lanzo una ''Excepción''. Siempre me pregunto: ¿Qué puede causar esta excepción ?, ¿cómo debe reaccionar mi programa ?, si hay un JavaDoc, tal vez se explique la causa, pero a veces hay una no se explica un javadoc o la excepción. Demasiada bruja de cabeza se puede evitar con un WellChossenExceptionTypeName
Tiendo a tener menos tipos de excepciones, aunque no es realmente la manera OO de hacerlo. En cambio, pongo una enumeración a mis Excepciones personalizadas, que clasifica la Excepción. La mayoría de las veces tengo una Excepción base personalizada, que se aferra a un par de miembros, que pueden anularse o personalizarse en los tipos de Excepción derivados.
Hace un par de meses publiqué en mi blog la idea de cómo internacionalizar Excepciones. Incluye algunas de las ideas mencionadas anteriormente.
Yo solo iría por
throw new exception("WhatCausedIt")
si desea manejar sus excepciones, podría pasar un código en lugar de "WhatCausedIt" y luego reaccionar a las diferentes respuestas con una declaración de cambio.
Creo que una combinación de lo anterior te dará el mejor resultado.
Puede lanzar diferentes excepciones según el problema. por ejemplo, falta la dirección de correo electrónico = ArgumentException.
Pero luego, en la capa UI puede verificar el tipo de excepción y, si es necesario, el mensaje y luego mostrar un mensaje apropiado al usuario. Personalmente, solo presento un mensaje informativo al usuario si se produce un cierto tipo de excepción (UserException en mi aplicación). Por supuesto, debe eliminar y verificar la entrada del usuario tanto como sea posible más arriba en la pila para asegurarse de que las excepciones sean generadas por escenarios realmente improbables, no como un filtro para correos electrónicos mal formados que se pueden verificar fácilmente con una expresión regular.
Tampoco me preocuparía las implicaciones de rendimiento de capturar una excepción de la entrada del usuario. La única vez que verá problemas de rendimiento a partir de excepciones es cuando se lanzan y atrapan en un bucle o similar.
Depende de si el código que capta la excepción necesita diferenciar entre excepciones o si solo está utilizando excepciones para fallar en una página de error. Si necesita diferenciar entre una excepción NullReference y su MailException personalizada más arriba en la pila de llamadas, dedique tiempo y escríbalo. Pero la mayoría de las veces los programadores usan las excepciones como una trampa para arrojar un error en la página web. En este caso, solo está desperdiciando esfuerzos al escribir una nueva excepción.
En lugar de usar excepciones, tiendo a devolver una lista de objetos de estado de métodos que pueden tener problemas para ejecutarse. Los objetos de estado contienen una enumeración de gravedad (información, advertencia, error, ...) un nombre de objeto de estado como "Dirección de correo electrónico" y un mensaje legible por el usuario como "Dirección de correo mal formateada"
El código de llamada luego decidiría qué filtrar a la interfaz de usuario y qué gestionar.
Personalmente, creo que las excepciones son estrictamente cuando no se puede implementar una solución de código normal. El golpe de rendimiento y las restricciones de manejo son demasiado para mí.
Otro motivo para usar una lista de objetos de estado es MUCHO más fácil identificar errores múltiples (como durante la validación). Después de todo, solo puede lanzar una excepción que debe manejarse antes de continuar.
Imagínese a un usuario que envía un correo electrónico que tenía una dirección de destino con formato incorrecto y un lenguaje contenido que está bloqueando. ¿Lanza la excepción de correo electrónico mal formado, luego, después de que lo arreglan y lo vuelven a enviar, lanza una excepción de lenguaje incorrecto? Desde la perspectiva de la experiencia del usuario, tratar con todos a la vez es una mejor manera de hacerlo.
ACTUALIZACIÓN: combina respuestas
@Jonathan: Mi punto es que puedo evaluar la acción, en este caso enviando un correo electrónico y enviando múltiples razones de falla. Por ejemplo, "dirección de correo electrónico incorrecta", "título del mensaje en blanco", etc.
Con una excepción, usted está limitado a filtrar el único problema y luego pedirle al usuario que lo vuelva a enviar y en ese momento se enterará de un segundo problema. Este es un diseño de IU realmente malo.
Reinventando la rueda ... posiblemente. Sin embargo, la mayoría de las aplicaciones deberían analizar toda la transacción para brindar la mejor información posible al usuario. Imagine si su compilador se detuvo en el primer error. Luego corriges el error y presionas compilar de nuevo solo para que se detenga nuevamente por un error diferente. Qué dolor en el trasero. Para mí, ese es exactamente el problema con arrojar excepciones y, por lo tanto, la razón por la que dije usar un mecanismo diferente.
Descubrí que, si necesita que el CÓDIGO decida qué hacer en función de la excepción devuelta, cree una excepción bien nombrada que clasifique como subclase un tipo de base común. El mensaje pasado debe considerarse "solo para ojos humanos" y demasiado frágil para tomar decisiones. Deje que el compilador haga el trabajo!
Si necesita pasar esto a una capa superior a través de un mecanismo que no conoce las excepciones comprobadas, puede envolverlo en una subclase con nombre adecuado de RuntimeException (MailDomainException) que puede alcanzarse en un nivel alto y actuar sobre la causa original.
En mi código, encuentro que las excepciones MOST se filtran hasta una capa de IU donde son capturadas por mis manejadores de excepciones que simplemente muestran un mensaje al usuario (y escriben en el registro). Es una excepción inesperada, después de todo.
A veces, quiero detectar una excepción específica (como parece querer hacer). Sin embargo, probablemente encontrará que esto es algo raro y que es indicativo de usar excepciones para controlar la lógica, que es ineficiente (lento) y a menudo desaprovechado.
Entonces, si usa su ejemplo, si desea ejecutar alguna lógica especial cuando el servidor de correo electrónico no está configurado, puede agregar un método al objeto emailUtil como:
public bool isEmailConfigured ()
... llame a eso primero, en lugar de buscar una excepción específica.
Cuando ocurre una excepción, realmente significa que la situación fue completamente inesperada y el código no puede manejarlo, por lo que lo mejor que puede hacer es informarlo al usuario (o escribirlo en un registro o reiniciarlo)
En cuanto a tener una jerarquía de excepciones frente a excepciones con códigos de error en ellas, normalmente hago lo último. Es más fácil agregar nuevas excepciones, si solo necesita definir una nueva constante de error en lugar de una clase completamente nueva. Pero, no importa mucho mientras intentes ser consistente a lo largo de tu proyecto.
Normalmente comienzo con una excepción general y la subclasé si es necesario. Siempre puedo detectar la excepción general (y con ella todas las excepciones subclasificadas) si es necesario, pero también la específica.
Un ejemplo de la API de Java es IOException, que tiene subclases como FileNotFoundException o EOFException (y mucho más).
De esta forma obtienes las ventajas de ambos, no tienes cláusulas throw como:
throws SpecificException1, SpecificException2, SpecificException3 ...
un general
throws GeneralException
es suficiente. Pero si desea tener una reacción especial ante circunstancias especiales, siempre puede detectar la excepción específica.