Pregunta

Estoy atascado decidiendo cómo manejar las excepciones en mi aplicación.

Mucho si mis problemas con excepciones provienen de 1) acceso a datos a través de un servicio remoto o 2) deserialización de un objeto JSON. Desafortunadamente, no puedo garantizar el éxito de ninguna de estas tareas (corte de conexión de red, objeto JSON con formato incorrecto que está fuera de mi control).

Como resultado, si encuentro una excepción, simplemente la detecto dentro de la función y devuelvo FALSE a la persona que llama. Mi lógica es que lo único que realmente le importa a la persona que llama es si la tarea fue exitosa, no por qué no lo fue.

Aquí hay un código de ejemplo (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 crear una excepción en una pila de llamadas?).

En resumen de preguntas clave:

  1. ¿Está bien solo detectar las excepciones pero no aumentarlas o notificar formalmente al sistema (ya sea a través de un registro o una notificación al usuario)?
  2. ¿Cuáles son las mejores prácticas para excepciones que no resultan en que todo lo que requiere un bloque try / catch?

Seguimiento / Editar

Gracias por todos los comentarios, encontré algunas fuentes excelentes sobre administración de excepciones en línea:

Parece que la administració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 gestionan las excepciones dentro de un sistema.

Además, tenga cuidado con la rotación de código mediante intentos / capturas excesivas o sin dar una excepción a su respeto (una excepción es advertir al sistema, ¿qué más se debe advertir?).

Además, este es un comentario bastante atractivo de m3rLinEz .

  

Tiendo a estar de acuerdo con Anders Hejlsberg y con usted en que la mayoría de las personas que llaman solo   cuidado si la operación es exitosa o no.

A partir de este comentario, surgen algunas preguntas para pensar cuando se trata de excepciones:

  • ¿Cuál es el punto en que se lanza esta excepción?
  • ¿Qué sentido tiene manejarlo?
  • ¿A la persona que llama realmente le importa la excepción o solo le importa si la llamada fue exitosa?
  • ¿Es forzoso que una persona que llama administre una posible excepción?
  • ¿Eres respetuoso con los idiomas del idioma?
    • ¿Realmente necesitas devolver una bandera de éxito como boolean? Devolver booleano (o un int) es más una mentalidad de C que un Java (en Java solo manejarías la excepción).
    • Siga las construcciones de administración de errores asociadas con el idioma :)!
¿Fue útil?

Solución

Me parece extraño que desee detectar excepciones y convertirlas en códigos de error. ¿Por qué crees que la persona que llama preferiría los códigos de error a las excepciones cuando esta última es la predeterminada en Java y C #?

En cuanto a sus preguntas:

  1. Solo debes capturar las excepciones que realmente puedas manejar. Sólo atrapar excepciones no es lo correcto en la mayoría de los casos. Hay algunas excepciones (por ejemplo, el registro y las excepciones de clasificación) entre hilos), pero incluso para esos casos, generalmente deberías volver a lanzar las excepciones.
  2. Definitivamente no deberías tener muchas declaraciones try / catch en tu código. Una vez más, la idea es solo detectar las excepciones que puede manejar. Puede incluir un controlador de excepciones superior para convertir cualquier no manejado excepciones en algo algo útil para el usuario final, pero De lo contrario no deberías intentar atrapar todas y cada una de las excepciones en todos los lugares posibles.

Otros consejos

Esto depende de la aplicación y la situación. Si está construyendo un componente de biblioteca, debería aumentar las excepciones, aunque deberían estar ajustadas para ser contextuales con su componente. Por ejemplo, si está creando una base de datos XML y supongamos que está utilizando el sistema de archivos para almacenar sus datos, y está utilizando los permisos del sistema de archivos para proteger los datos. No querrá hacer una burbuja de una excepción de FileIOAccessDenied ya que eso filtra su implementación. En su lugar, ajustaría la excepción y lanzaría un error AccessDenied. Esto es especialmente cierto si distribuye el componente a terceros.

En cuanto a si está bien tragar excepciones. Eso depende de tu sistema. Si su aplicación puede manejar los casos de falla y no hay ningún beneficio al notificar al usuario por qué falló, entonces siga adelante, aunque le recomiendo encarecidamente que registre la falla. Siempre me ha parecido frustante que me llamen para ayudar a solucionar un problema y descubrir que estaban tragando la excepción (o reemplazándola y lanzando una nueva en su lugar sin establecer la excepción interna).

En general yo uso las siguientes reglas:

  1. En mis componentes & amp; bibliotecas Solo detecto una excepción si tengo la intención de manejarlo o hacer algo basado en él. O si quiero proporcionar información contextual adicional en una excepción.
  2. Utilizo una captura de intento general en el punto de entrada de la aplicación, o el nivel más alto posible. Si llega una excepción, simplemente la registro y la dejo. Lo ideal es que las excepciones nunca lleguen aquí.

El siguiente código me parece un olor:

try
{
    //do something
}
catch(Exception)
{
   throw;
}

Este código no sirve para nada y no debe incluirse.

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 comprobada de Java.

Fallos y excepciones

También hay excelentes recursos en la parte inferior 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.

  

Bill Venners : mencionaste   problemas de escalabilidad y versiones   con respecto a las excepciones comprobadas.   ¿Podría aclarar lo que quiere decir con   esos dos problemas?

     

Anders Hejlsberg : Comencemos con   versionado, porque los problemas son   Bastante fácil de ver allí. Digamos que yo   Crea un método foo que lo declare.   lanza las excepciones A, B y C. En   versión dos de foo, quiero agregar un   Un montón de características, y ahora foo podría   Lanzar excepción D. Es una ruptura.   Cambia para que yo agregue D a los tiros.   cláusula de ese método, porque   llamador existente de ese método será   casi seguro que no manejamos eso   excepción.

     

Añadiendo una nueva excepción a un lanzamiento   cláusula en una nueva versión rompe el cliente   código. Es como agregar un método a un   interfaz. Después de publicar un   Interfaz, es para todos los prácticos.   Fines inmutables, porque cualquier   su implementación podría tener la   métodos que desea agregar en el   siguiente version Así que tienes que crear   una nueva interfaz en su lugar. similar   con excepciones, o bien tendrías   para crear un nuevo método llamado   foo2 que lanza más excepciones, o   Tendrías que atrapar la excepción D en   el nuevo foo, y transformar la d en   una A, B o C.

     

Bill Venners : ¿Pero no estás rompiendo?   Su código en ese caso de todos modos, incluso   en un idioma sin comprobar   excepciones? Si la nueva versión de foo   va a lanzar una nueva excepción que   los clientes deben pensar en el manejo,   no es su código roto solo por el   hecho de que no esperaban eso   ¿excepción cuando escribieron el código?

     

Anders Hejlsberg : No, porque en muchos   De los casos, a la gente no le importa. Ellos son   no voy a manejar ninguno de estos   excepciones Hay un nivel inferior   manejador de excepciones alrededor de su mensaje   lazo. Ese manejador solo va a   abrir un diálogo que dice lo que pasó   mal y continuar Los programadores   proteger su código por escrito intentar   finalmente está en todas partes, por lo que van a volver   fuera correctamente si se produce una excepción,   pero no están realmente interesados ??en   manejando las excepciones.

     

La cláusula de tiros, al menos la forma   Se implementa en Java, no lo hace.   necesariamente obligarte a manejar el   excepciones, pero si no manejas   ellos te obligan a reconocer   precisamente qué excepciones pueden pasar   mediante. Requiere que usted o   atrapar excepciones declaradas o ponerlas   en su propia cláusula de lanzamientos. Trabajar   En torno a este requisito, la gente lo hace.   Cosas ridículas. Por ejemplo, ellos   Decora cada método con, " tiros.   Excepción. & Quot; Que solo completamente   derrotas la característica, y acabas de hacer   el programador escribe mas gobbledy   gunk Eso no ayuda a nadie.

EDITAR: Se agregaron más detalles sobre la conversación

Las excepciones comprobadas son un tema controvertido en general, y en Java en particular (más adelante trataré de encontrar algunos ejemplos para los que están a favor y en contra de ellos).

Como regla general, el manejo de excepciones debe ser algo en torno a estas directrices, sin ningún orden en particular:

  • Por razones de mantenibilidad, siempre registre las excepciones para que cuando comience a ver los errores, el registro lo ayude a señalar el lugar donde probablemente se inició el error. Nunca deje printStackTrace () o similares, es probable que uno de sus usuarios obtenga uno de esos rastros de pila eventualmente y tenga exactamente cero conocimientos sobre qué hacer con ella.
  • Capture las excepciones que puede manejar, y solo esas, y las maneje , no las tire de la pila.
  • Siempre captura una clase de excepción específica y, en general, nunca debes capturar el tipo Exception , es muy probable que trates las excepciones importantes.
  • ¡Nunca (nunca) atrape Error s !! , lo que significa: Nunca atrape Throwable s como Error s son subclases de este último. Los errores son problemas que probablemente nunca podrá manejar (por ejemplo, OutOfMemory 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 puede devolver falso, pero asegúrate de que los lugares a los que llamas ese método puedan manejarlo.

Solo debes capturar las excepciones con las que puedes lidiar. Por ejemplo, si trata de leer a través de una red y la conexión se agota y obtiene una excepción, puede volver a intentarlo. Sin embargo, si está leyendo a través de una red y obtiene una excepción IndexOutOfBounds, realmente no puede manejar eso porque no (bueno, en este caso, no) sabe qué lo causó. Si va a devolver falso o -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 está fuera de la memoria.

Las excepciones son errores que no forman parte de la ejecución normal del programa. Dependiendo de lo que haga su programa y de sus usos (es decir, un procesador de textos frente a un monitor cardíaco), querrá hacer cosas diferentes cuando encuentre una excepción. He trabajado con código que utiliza excepciones como parte de la ejecución normal y definitivamente es un olor de código.

Ej.

try
{
   sendMessage();

   if(message == success)
   {
       doStuff();
   }
   else if(message == failed)
   {
       throw;
   }
}
catch(Exception)
{
    logAndRecover();
}

Este código me hace ladrar. En mi opinión, no debe recuperarse de las excepciones a menos que sea un programa crítico. Si tus excepciones de lanzamiento están pasando cosas malas.

Todo lo anterior parece razonable, y con frecuencia su lugar de trabajo puede tener una política. En nuestro lugar, hemos definido los tipos de Excepción: SystemException (sin marcar) y ApplicationException (marcado).

Hemos acordado que es poco probable que las SystemException sean recuperables y que se manejen una vez en la parte superior. Para proporcionar más contexto, nuestros SystemException se amplían para indicar dónde ocurrieron, por ejemplo. RepositoryException , ServiceEception , etc.

ApplicationException s podría tener un significado comercial como InsufficientFundsException y debe ser manejado por el código del cliente.

Witohut 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 tragar una excepción, pero debe decidir por qué, y siempre registrar el evento y el seguimiento de pila. Por último, como su método no tiene otro proceso, es bastante redundante (¿excepto para la encapsulación?), Por lo que doactualStuffOnObject (p_jsonObject); podría devolver un booleano!

Después de reflexionar y mirar tu código, me parece que simplemente estás volviendo a lanzar la excepción como un booleano. Solo puede dejar pasar el método a través de la excepción (ni siquiera tiene que atraparlo) y lidiar con él 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 detecte la excepción.

A veces puede suceder que la excepción con la que te encuentras no tenga sentido para la persona que llama (es decir, es una excepción de la red), en cuyo caso deberías incluirla en una excepción específica del dominio.

Si, por otro lado, la excepción señala un error irrecuperable en su programa (es decir, el resultado final de esta excepción será la terminación del programa) Personalmente, me gustaría hacerlo explícito detectándolo y lanzando una excepción en tiempo de ejecución.

Si va a utilizar el patrón de código en su ejemplo, llámelo TryDoSomething y solo detecte excepciones específicas.

También considera usar un Filtro de excepciones al registrar excepciones con fines de diagnóstico. VB tiene soporte de idioma para los 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 para la depuración en lugar de la captura y el rebrote. 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 rethrow corta la pila en el punto en que se volvió a derribar.

Los casos en los que TryXXXX tiene sentido son cuando está envolviendo una función de terceros que se ejecuta en casos que no son realmente excepcionales, o que son difíciles de probar sin llamar a la función. Un ejemplo sería algo como:

// throws NumberNotHexidecimalException
int ParseHexidecimal(string numberToParse); 

bool TryParseHexidecimal(string numberToParse, out int parsedInt)
{
     try
     {
         parsedInt = ParseHexidecimal(numberToParse);
         return true;
     }
     catch(NumberNotHexidecimalException ex)
     {
         parsedInt = 0;
         return false;
     }
     catch(Exception ex)
     {
         // Implement the error policy for unexpected exceptions:
         // log a callstack, assert if a debugger is attached etc.
         LogRetailAssert(ex);
         // rethrow the exception
         // The downside is that a JIT debugger will have the next
         // line as the place that threw the exception, rather than
         // the original location further down the stack.
         throw;
         // A better practice is to use an exception filter here.
         // see the link to Exception Filter Inject above
         // http://code.msdn.microsoft.com/ExceptionFilterInjct
     }
}

Si usas un patrón como TryXXX o no es más una pregunta de estilo. La cuestión de atrapar todas las excepciones y de tragarlas no es un problema de estilo. ¡Asegúrate de que las excepciones inesperadas puedan propagarse!

Sugiero que sigas tus indicaciones de la biblioteca estándar para el idioma que estás utilizando. No puedo hablar por C #, pero veamos Java.

Por ejemplo, java.lang.reflect.Array tiene un método estático set :

static void set(Object array, int index, Object value);

La forma C sería

static int set(Object array, int index, Object value);

... con el valor de retorno siendo un indicador de éxito. Pero ya no estás en el mundo C

Una vez que adopta las excepciones, debe descubrir 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 bloque try .

Como han señalado otros: debe ser lo más específico posible en el tipo de excepción que detecte.

Si va a capturar una excepción y devolver falso, debería ser una excepción muy específica. No estás haciendo eso, estás atrapándolos a todos y regresando falso. Si obtengo una excepción MyCarIsOnFireException, ¡quiero saberlo de inmediato! El resto de las excepciones que no me importa. Por lo tanto, debe tener una pila de controladores de excepciones que digan "whoa whoa algo está mal aquí" para algunas excepciones (volver a lanzar, o atrapar y volver a emitir una nueva excepción que explique mejor lo que sucedió) y simplemente devuelva falso para otros.

Si este es un producto que lanzará, debería registrar esas excepciones en algún lugar, lo ayudará a mejorar las cosas en el futuro.

Edit: En cuanto a la cuestión de envolver todo en un try / catch, creo que la respuesta es sí. Las excepciones deben ser tan raras en su código que el código en el bloque catch se ejecuta tan raramente que no afecta al rendimiento en absoluto. Una excepción debe ser un estado en el que su máquina de estados se haya roto y no sepa 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 capturada. " Excepción en el método doSomeStuff () " no es muy útil para nadie que tenga que averiguar por qué se rompió mientras está de vacaciones (o en un nuevo empleo).

Mi estrategia:

Si la función original devolvió void , la cambio para devolver bool . Si se produjo una excepción / error, devuelva false , si todo fue correcto, devuelva true .

Si la función debería devolver algo, entonces, cuando se produjo una excepción / error, devuelva nulo , de lo contrario, el elemento retornable.

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.

Algunas respuestas excelentes aquí. Me gustaría agregar, que si terminas con algo como lo que publicaste, al menos imprime 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 pelear.

los bloques try / catch forman un segundo conjunto de lógica incrustada en el primer conjunto (principal), ya que son una excelente manera de eliminar el código de espagueti ilegible y difícil de depurar.

Aún así, si se usan razonablemente, funcionan de maravilla en la legibilidad, pero solo debes seguir dos reglas simples:

  • utilícelos (con moderación) en el nivel bajo para detectar problemas de manejo de la biblioteca y transmitirlos de nuevo al flujo lógico principal. La mayor parte del manejo de errores que queremos, debe provenir del código en sí, como parte de los datos en sí. ¿Por qué crear condiciones especiales, si los datos devueltos no son especiales?

  • use un controlador grande 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 de código de prueba / captura y objetos de error. De esa manera, funciona de manera simple y como se espera, sin importar dónde la use o qué haga con ella.

Paul.

Puede que llegue un poco tarde con la respuesta, pero el manejo de errores es algo que siempre podemos cambiar y evolucionar a lo largo del tiempo. Si quieres leer algo más sobre este tema, escribí una publicación en mi nuevo blog sobre este tema. http://taoofdevelopment.wordpress.com

Codificación feliz.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top