Estoy atascado decidiendo cómo manejar las excepciones en mi aplicación.
Mucho si mis problemas con las excepciones provienen de 1) acceder a los datos a través de un servicio remoto o 2) deserializar un objeto JSON. Desafortunadamente, no puedo garantizar el éxito de ninguna de estas tareas (cortar la conexión de red, objeto JSON mal formado que está fuera de mi control).
Como resultado, si encuentro una excepción, simplemente la atrapo dentro de la función y devuelvo FALSE a la persona que llama. Mi lógica es que todo lo que realmente le importa a la persona que llama es si la tarea fue exitosa, no por qué no fue exitosa.
Aquí hay un código de muestra (en JAVA) de un método típico)
public boolean doSomething(Object p_somthingToDoOn)
{
boolean result = false;
try{
// if dirty object then clean
doactualStuffOnObject(p_jsonObject);
//assume success (no exception thrown)
result = true;
}
catch(Exception Ex)
{
//don't care about exceptions
Ex.printStackTrace();
}
return result;
}
Creo que este enfoque está bien, pero tengo mucha curiosidad por saber cuáles son las mejores prácticas para administrar excepciones (¿debería realmente hacer burbujear una excepción en una pila de llamadas?).
En resumen de preguntas clave:
- ¿Está bien simplemente detectar las excepciones pero no hacerlas burbujear o notificar formalmente al sistema (ya sea a través de un registro o una notificación al usuario)?
- ¿Qué mejores prácticas existen para las excepciones que no dan como resultado que todo requiera un bloque try / catch?
Seguimiento / Editar
Gracias por todos los comentarios, encontré algunas fuentes excelentes sobre la gestión de excepciones en línea:
- Mejores prácticas para el manejo de excepciones | O'Reilly Media
- Prácticas recomendadas para el manejo de excepciones en .NET
- Mejores prácticas: gestión de excepciones (el artículo ahora apunta a la copia de archive.org)
- Antipatrones de manejo de excepciones
Parece que la gestión de excepciones es una de esas cosas que varían según el contexto. Pero lo más importante es que uno debe ser coherente en la forma en que administran las excepciones dentro de un sistema.
Además, tenga cuidado con la descomposición del código a través de intentos / capturas excesivos o no otorgando una excepción a su respeto (una excepción es una advertencia al sistema, ¿qué más se debe advertir?).
Además, este es un bonito comentario de elección de m3rLinEz .
Tiendo a estar de acuerdo con Anders Hejlsberg y usted en que a la mayoría de las personas que llaman solo les importa si la operación es exitosa o no.
A partir de este comentario, surgen algunas preguntas en las que pensar cuando se trata de excepciones:
- ¿Cuál es el punto de lanzar esta excepción?
- ¿Cómo tiene sentido manejarlo?
- ¿A la persona que llama realmente le importa la excepción o simplemente le importa si la llamada fue exitosa?
- ¿Es correcto forzar a una persona que llama a administrar una posible excepción?
- ¿Estás siendo respetuoso con los ídolos del idioma?
- ¿Realmente necesitas devolver un indicador de éxito como booleano? Devolver booleano (o un int) es más una mentalidad de C que una de Java (en Java, solo manejaría la excepción).
- Siga las construcciones de gestión de errores asociadas con el idioma :)!
fuente
Respuestas:
Me parece extraño que desee detectar excepciones y convertirlas en códigos de error. ¿Por qué cree que la persona que llama preferiría los códigos de error a las excepciones cuando esta última es la predeterminada tanto en Java como en C #?
En cuanto a tus preguntas:
fuente
Depende de la aplicación y la situación. Si está construyendo un componente de biblioteca, debe incluir excepciones, aunque deben ajustarse para que sean contextuales con su componente. Por ejemplo, si está creando una base de datos Xml y digamos que está utilizando el sistema de archivos para almacenar sus datos, y está utilizando permisos del sistema de archivos para proteger los datos. No querrá hacer brotar una excepción FileIOAccessDenied ya que eso filtra su implementación. En su lugar, envolvería la excepción y lanzaría un error de AccessDenied. Esto es especialmente cierto si distribuye el componente a terceros.
En cuanto a si está bien aceptar excepciones. Eso depende de tu sistema. Si su aplicación puede manejar los casos de falla y no hay ningún beneficio en notificar al usuario por qué falló, continúe, aunque le recomiendo que registre la falla. Siempre me ha resultado frustrante ser llamado para ayudar a solucionar un problema y descubrir que se estaban tragando la excepción (o reemplazándola y lanzando una nueva en su lugar sin establecer la excepción interna).
En general, utilizo las siguientes reglas:
Encuentro que el siguiente código es un olor:
Un código como este no sirve de nada y no debe incluirse.
fuente
// do something
incluyetry/finally
bloques alrededor del punto que lanza, losfinally
bloques se ejecutarán antes que elcatch
bloque. Sin eltry/catch
, la excepción volará hasta la parte superior de la pila sin quefinally
se ejecute ningún bloque. Esto permite que el controlador de nivel superior decida si ejecutar o no losfinally
bloques.Me gustaría recomendar otra buena fuente sobre el tema. Es una entrevista con los inventores de C # y Java, Anders Hejlsberg y James Gosling respectivamente, sobre el tema de la excepción marcada de Java.
Fallos y excepciones
También hay excelentes recursos al final de la página.
Tiendo a estar de acuerdo con Anders Hejlsberg y usted en que a la mayoría de las personas que llaman solo les importa si la operación es exitosa o no.
EDITAR: Se agregaron más detalles sobre la conversión.
fuente
FetchData
y arroja una excepción de un tipo inesperado, no tiene forma de saber si esa excepción simplemente significa que los datos no están disponibles (en cuyo caso la capacidad del código para funcionar sin ellos lo "resolvería"), o si significa que la CPU está en llamas y el sistema debe realizar un "apagado de seguridad" en la primera oportunidad. Parece que el Sr. Hejlsberg está sugiriendo que el código debería asumir lo primero; quizás esa sea la mejor estrategia posible dadas las jerarquías de excepciones existentes, pero no obstante, parece asqueroso.Las excepciones marcadas son un tema controvertido en general, y en Java en particular (más adelante intentaré encontrar algunos ejemplos para quienes están a favor y en contra).
Como reglas generales, el manejo de excepciones debe basarse en estas pautas, sin ningún orden en particular:
printStackTrace()
o algo así, es probable que uno de tus usuarios obtenga uno de esos rastros de pila eventualmente y no tenga exactamente ningún conocimiento sobre qué hacer con él.Exception
, es muy probable que se trague excepciones importantes.Error
s !! , lo que significa: Nunca capturasThrowable
s ya queError
s son subclases de este último.Error
s son problemas que probablemente nunca podrá manejar (por ejemploOutOfMemory
, u otros problemas de JVM)Con respecto a su caso específico, asegúrese de que cualquier cliente que llame a su método reciba el valor de retorno adecuado. Si algo falla, un método de retorno booleano podría devolver falso, pero asegúrese de que los lugares a los que llama a ese método puedan manejar eso.
fuente
Solo debe detectar las excepciones con las que pueda lidiar. Por ejemplo, si está leyendo en una red y la conexión se agota y obtiene una excepción, puede intentarlo de nuevo. Sin embargo, si está leyendo en una red y obtiene una excepción IndexOutOfBounds, realmente no puede manejar eso porque no sabe (bueno, en este caso no) sabe qué lo causó. Si va a devolver falso, -1 o nulo, asegúrese de que sea para excepciones específicas. No quiero que una biblioteca que estoy usando devuelva un falso en una lectura de red cuando la excepción lanzada es que el montón no tiene memoria.
fuente
Las excepciones son errores que no forman parte de la ejecución normal del programa. Dependiendo de lo que haga su programa y sus usos (es decir, un procesador de texto frente a un monitor cardíaco), querrá hacer cosas diferentes cuando encuentre una excepción. He trabajado con código que usa excepciones como parte de la ejecución normal y definitivamente es un olor a código.
Ex.
Este código me hace vomitar. En mi opinión, no debe recuperarse de las excepciones a menos que sea un programa crítico. Si lanza excepciones, entonces están sucediendo cosas malas.
fuente
Todo lo anterior parece razonable y, a menudo, su lugar de trabajo puede tener una política. En nuestro lugar hemos definido tipos de excepción:
SystemException
(sin marcar) yApplicationException
(marcada).Hemos acordado que
SystemException
es poco probable que los correos electrónicos sean recuperables y que se manejarán una vez en la parte superior. Para proporcionar más contexto, nuestrosSystemException
s se exteneded para indicar dónde se produjeron, por ejemploRepositoryException
,ServiceEception
, etc.ApplicationException
s podría tener un significado comercial comoInsufficientFundsException
y debe manejarse mediante el código del cliente.Sin un ejemplo concreto, es difícil comentar sobre su implementación, pero nunca usaría códigos de retorno, son un problema de mantenimiento. Puede tragarse una excepción, pero debe decidir por qué y siempre registrar el evento y el seguimiento de la pila. Por último, como su método no tiene otro procesamiento, es bastante redundante (¿excepto para la encapsulación?), ¡Por lo que
doactualStuffOnObject(p_jsonObject);
podría devolver un booleano!fuente
Después de pensar un poco y mirar su código, me parece que simplemente está volviendo a generar la excepción como booleana. Puede dejar que el método pase esta excepción (ni siquiera tiene que detectarla) y tratarla en la persona que llama, ya que ese es el lugar donde importa. Si la excepción hará que la persona que llama vuelva a intentar esta función, la persona que llama debe ser la que reciba la excepción.
A veces puede suceder que la excepción que está encontrando no tenga sentido para la persona que llama (es decir, es una excepción de red), en cuyo caso debe incluirla en una excepción específica de dominio.
Si, por otro lado, la excepción indica un error irrecuperable en su programa (es decir, el resultado final de esta excepción será la terminación del programa), personalmente me gusta hacer eso explícito al detectarlo y lanzar una excepción de tiempo de ejecución.
fuente
Si va a utilizar el patrón de código en su ejemplo, llámelo TryDoSomething y capture solo excepciones específicas.
También considere usar un filtro de excepciones al registrar excepciones con fines de diagnóstico. VB tiene soporte de idioma para filtros de excepción. El enlace al blog de Greggm tiene una implementación que se puede usar desde C #. Los filtros de excepción tienen mejores propiedades de depuración que la captura y el relanzamiento. Específicamente, puede registrar el problema en el filtro y dejar que la excepción continúe propagándose. Ese método permite adjuntar un depurador JIT (Just in Time) para tener la pila original completa. Un nuevo lanzamiento corta la pila en el punto en que se volvió a lanzar.
Los casos en los que TryXXXX tiene sentido son cuando está ajustando una función de terceros que arroja casos que no son realmente excepcionales o que son simples y difíciles de probar sin llamar a la función. Un ejemplo sería algo como:
Si usa un patrón como TryXXX o no, es más una cuestión de estilo. La cuestión de captar todas las excepciones y tragarlas no es una cuestión de estilo. ¡Asegúrese de que se permita la propagación de excepciones inesperadas!
fuente
Sugiero tomar sus pistas de la biblioteca estándar para el idioma que está usando. No puedo hablar por C #, pero veamos Java.
Por ejemplo, java.lang.reflect.Array tiene un
set
método estático :La forma C sería
... siendo el valor de retorno un indicador de éxito. Pero ya no estás en el mundo C.
Una vez que acepte las excepciones, debería encontrar que hace que su código sea más simple y claro, al alejar su código de manejo de errores de su lógica central. Trate de tener muchas declaraciones en un solo
try
bloque.Como han señalado otros, debe ser lo más específico posible en el tipo de excepción que detecta.
fuente
Si va a detectar una excepción y devolver falso, debería ser una excepción muy específica. No estás haciendo eso, los estás capturando a todos y devolviendo falsos. Si obtengo una MyCarIsOnFireException, ¡quiero saberlo de inmediato! El resto de las excepciones puede que no me importe. Por lo tanto, debería tener una pila de manejadores de excepciones que digan "whoa whoa, algo está mal aquí" para algunas excepciones (volver a lanzar, o capturar y volver a lanzar una nueva excepción que explique mejor lo que sucedió) y simplemente devolver falso para otras.
Si este es un producto que va a lanzar, debería registrar esas excepciones en algún lugar, lo ayudará a ajustar las cosas en el futuro.
Editar: En cuanto a la cuestión de envolver todo en un intento / captura, creo que la respuesta es sí. Las excepciones deberían ser tan raras en su código que el código en el bloque de captura se ejecuta tan raramente que no afecta el rendimiento en absoluto. Una excepción debería ser un estado en el que su máquina de estado se rompió y no sabe qué hacer. Al menos, vuelva a generar una excepción que explique lo que estaba sucediendo en ese momento y que contenga la excepción detectada. "Excepción en el método doSomeStuff ()" no es muy útil para cualquiera que tenga que averiguar por qué se rompió mientras está de vacaciones (o en un nuevo trabajo).
fuente
Mi estrategia:
Si la función original devolvió vacío , la cambio para devolver bool . Si se produjo una excepción / error, devuelva falso , si todo estuvo bien, devuelva verdadero .
Si la función debe devolver algo, cuando se produzca una excepción / error, devuelva nulo ; de lo contrario, el artículo devuelto .
En lugar de bool, se podría devolver una cadena que contenga la descripción del error.
En todos los casos, antes de devolver algo, registre el error.
fuente
Algunas excelentes respuestas aquí. Me gustaría agregar que si termina con algo como lo que publicó, al menos imprima más que el seguimiento de la pila. Di lo que estabas haciendo en ese momento, y Ex.getMessage (), para darle al desarrollador una oportunidad de luchar.
fuente
Los bloques try / catch forman un segundo conjunto de lógica incrustado sobre el primer conjunto (principal), como tal, son una excelente manera de eliminar códigos espaguetis ilegibles y difíciles de depurar.
Aún así, si se usan razonablemente, funcionan de maravilla en cuanto a legibilidad, pero debes seguir dos reglas simples:
úselos (con moderación) en el nivel bajo para detectar problemas de manejo de bibliotecas y transmitirlos de nuevo al flujo lógico principal. La mayor parte del manejo de errores que queremos debe provenir del propio código, como parte de los datos en sí. ¿Por qué establecer condiciones especiales si los datos que se devuelven no son especiales?
use un gran controlador en el nivel superior para administrar cualquiera o todas las condiciones extrañas que surgen en el código que no se detectan en un nivel bajo. Haga algo útil con los errores (registros, reinicios, recuperaciones, etc.).
Aparte de estos dos tipos de manejo de errores, todo el resto del código en el medio debe estar libre y libre de códigos de prueba / captura y objetos de error. De esa manera, funciona de manera simple y como se espera sin importar dónde lo use o qué haga con él.
Pablo.
fuente
Puede que llegue un poco tarde con la respuesta, pero el manejo de errores es algo que siempre podemos cambiar y evolucionar con el tiempo. Si quieres leer algo más sobre este tema, escribí un post en mi nuevo blog al respecto. http://taoofdevelopment.wordpress.com
Codificación feliz.
fuente