2008-12-02 16 views
25

He estado programando durante los últimos 3 años. Cuando programo, utilizo para manejar todas las excepciones conocidas y alertar al usuario con gracia. Recientemente he visto un código que tiene casi todos los métodos incluidos en los bloques try/catch. El autor dice que es parte de la programación defensiva. Me pregunto, ¿esta es una programación realmente defensiva? ¿Recomiendas poner todo su código en bloques de prueba?¿Envolver todo en bloques try/catch constituye una programación defensiva?

+6

No estoy seguro de eso. La pregunta, aunque tiene un título genérico, en realidad es bastante específica sobre el uso de excepciones como método de programación defensiva, y cuáles son las preocupaciones al hacerlo. – Elie

Respuesta

52

Mi regla básica es: A menos que pueda corregir el problema que causó la excepción, no la capte, permita que suba hasta un nivel en el que pueda tratarse.

En mi experiencia, el 95% de todos los bloques catch ignoran la excepción (catch {}) o simplemente registran el error y vuelven a lanzar la excepción. Esto último puede parecer lo correcto, pero en la práctica, cuando se hace en todos los niveles, simplemente terminas con tu registro lleno de cinco copias del mismo mensaje de error. Por lo general, estas aplicaciones tienen una "captura de ignorar" en el nivel más alto (ya que "tenemos try/catch en todos los niveles inferiores"), lo que resulta en una aplicación muy lenta con muchas excepciones perdidas y un registro de error demasiado largo para cualquiera que esté dispuesto a revisarlo.

+7

Exactamente. Poner todo en una declaración try/catch suele ser un signo de un desarrollador sin experiencia que no sabe mucho acerca de las excepciones IME. –

+0

Deberíamos permitirnos modificar + 2 si gastamos puntos de representación como downmodding. Pero lo haré aquí. –

+2

Creo que a veces, especialmente al cruzar el límite de un módulo o biblioteca, es una buena práctica capturar la excepción, envolverla en un tipo diferente de excepción (como las instalaciones de encadenamiento de causa de Java) y volver a lanzar. – rmeador

5

Existe tal cosa como el procesamiento "demasiado", y atrapando todas las excepciones kindof derrota el punto. Específicamente para C++, la declaración catch (...) captura todas las excepciones pero no puede procesar el contenido de esa excepción porque no conoce el tipo de la excepción (y podría ser cualquier cosa).

Debe detectar las excepciones que puede manejar total o parcialmente, volviendo a lanzar las excepciones parciales. No debería captar ninguna excepción que no pueda manejar, porque eso solo ofuscará los errores que pueden (o más bien) le morderán más adelante.

3

Supongo que la verdadera respuesta es "Depende". Si los bloques de prueba capturan excepciones muy genéricas, yo diría que es programación defensiva de la misma manera que nunca salir de su vecindario es conducir a la defensiva. Un try-catch (imo) se debe adaptar a excepciones específicas.

Una vez más, esta es solo mi opinión, pero mi concepto de programación defensiva es que necesitas menos/más pequeños bloques de prueba, no más/más grandes. Su código debería estar haciendo todo lo posible para asegurarse de que una condición de excepción nunca pueda existir en primer lugar.

4

Recomendaría en contra de esta práctica. Poner código en bloques de prueba cuando conoces los tipos de excepciones que se pueden arrojar es una cosa. Le permite, como indicó, recuperar y/o alertar graciosamente al usuario sobre el error. Sin embargo, poner todo su código dentro de dichos bloques donde no se sabe cuál es el error que puede ocurrir es usar excepciones para controlar el flujo del programa, que es un gran no-no.

Si está escribiendo un código bien estructurado, sabrá sobre cada excepción que pueda ocurrir, y puede detectarlas específicamente. Si no sabe cómo se puede lanzar una excepción en particular, no la capte, por si acaso. Cuando sucede, puede averiguar la excepción, qué lo causó y luego atraparlo.

8

La captura de excepciones al azar es mala. ¿Entonces que?

  • ¿Los ignorar? Excelente. Déjame saber cómo funciona eso para ellos.
  • ¿Iniciar sesión y seguir funcionando? Yo creo que no.
  • ¿Lanzar una excepción diferente como parte de un bloqueo? Buena suerte depurando eso.

La captura de excepciones para las que realmente puede hacer algo significativo es buena. Estos casos son fáciles de identificar y mantener.

+0

¿Qué es difícil de depurar sobre el n. ° 3? –

+0

@Bart van Heukelom: la excepción diferente que enmascara la excepción original hace que los registros sean confusos. El mensaje de registro es "SomeRandomException". La causa raíz es ValueError. Además, el rastreo original puede perderse fácilmente en el embrollo de plantear una nueva excepción. Intentalo. –

+0

Para eso sirve el encadenamiento de causa de Java. Asumo que otros idiomas tienen esto también (sé que incluso PHP 5.3 lo hace). –

10

El término "programación defensiva" significa escribir código de forma que pueda recuperarse de situaciones de error o que evite por completo la situación de error. Por ejemplo:

private String name; 

public void setName(String name) { 
} 

¿Cómo manejas name == null? ¿Lanzas una excepción o la aceptas? Si no tiene sentido tener un objeto sin nombre, entonces debe lanzar una excepción. ¿Qué pasa con el nombre == ""?

Pero ... luego escribes un editor. Mientras configura la IU, encuentra que hay situaciones en las que un usuario puede decidir quitar el nombre o el nombre puede quedar vacío mientras el usuario lo edita.

Otro ejemplo:

public boolean isXXX (String s) { 
} 

Aquí, la estrategia defensiva es a menudo para volver nula falsa cuando s == (NPE evitar cuando sea posible).

O:

public String getName() { 
} 

Un programador podría volver a la defensiva "" si el nombre == null para evitar NPE en código de llamada.

6

¿Puedo decir como un lado aquí que cada vez que uno de mis compañeros escribe una firma de método con "throws Exception" en lugar de enumerar los tipos de excepciones que realmente arroja el método, quiero ir y dispararles ¿en la cabeza? El problema es que después de un tiempo tienes 14 niveles de llamadas que dicen "lanza excepción", por lo que refactorizar para que declaren lo que realmente lanzan es un ejercicio importante.

+0

¡De acuerdo de todo corazón! Metafóricamente, por supuesto. En realidad no aboga por dispararle a nadie en ninguna parte de su persona. –

+0

Simplemente escriba un método llamado shootProgrammer (Programmer p) throws OutOfAmmoException – Elie

+2

@Bill - ¿está bien si los golpeo con un periódico enrollado entonces? –

9

Si va a manejar excepciones al azar, manejarlas en un solo lugar - la parte superior de la aplicación, a los efectos de:

  • que presentan un mensaje amigable para el usuario, y
  • guardando los diagnósticos.

Para todo lo demás, desea que el, la ubicación específica del bloqueo más inmediata posible, por lo que se puede contraer estas cosas tan pronto como sea posible - de lo contrario el manejo de excepciones se convierte en una forma de ocultar el diseño y el código descuidado.

En la mayoría de los casos donde la excepción es predecible, es posible realizar una prueba de antemano, para la condición que atrapará el controlador de excepción.

En general, si ... Else es mucho mejor que Try ... Catch.

+0

No estoy de acuerdo. Hay excepciones que maneja de esa manera, pero hay excepciones que maneja en profundidad: por ejemplo, parte de mi código arroja una "ScheduleConflictException", y algunos niveles más arriba hay un código para realizar cambios en el cronograma y volver a intentarlo. –

+0

@Paul, "En la mayoría de los casos", y creo que las excepciones personales son una excepción. En este caso, está creando una excepción para manejar más arriba, y no veo esto como lo que cris5gd estaba considerando como un caso general. –

+1

@Paul Y este suena como que no debería ser una excepción en absoluto ... –

14

No, no es "programación defensiva". Su compañero de trabajo está tratando de racionalizar su mal hábito mediante el empleo de una palabra de moda para un buen hábito.

Lo que está haciendo debe llamarse "barrerlo debajo de la alfombra". Es uniformemente (void) -el valor de retorno del estado de error de las llamadas a métodos.

+1

¿Estoy mostrando mi edad diciendo que me recuerda de inmediato la impresión impresa "no es una máquina de escribir" sin importar lo que errno fue? –

18

Uso extensivo de Try ... Catch no es programación defensiva, es solo clavando el cadáver en posición vertical.

Probar ... Por último, puede utilizarse ampliamente para la recuperación frente a excepciones inesperadas. Solo si espera una excepción y ahora cómo manejarla debería usar Try..Catch en su lugar.

A veces veo Try..Catch System.Exception, donde el bloque de captura simplemente registra la excepción y vuelve a lanzar. Hay al menos 3 problemas con ese enfoque:

  • Retirar supone una excepción no controlada y, por lo tanto, que el programa debe finalizar porque se encuentra en un estado desconocido. Pero catch causa la ejecución de los bloques Finally debajo del bloque Catch. En una situación indefinida, el código en estos bloques Finally podría empeorar el problema.
  • El código en esos bloques Finally cambiará el estado del programa. Por lo tanto, cualquier registro no capturará el estado real del programa cuando se lanzó originalmente la excepción. Y la investigación será más difícil porque el estado ha cambiado.
  • Te da una experiencia de depuración miserable, porque el depurador se detiene en el nuevo lanzamiento, no el lanzamiento original.
+5

+1 para la analogía 'clavar el cadáver' – ChrisA

2

He encontrado que los bloques "try" "catch" son muy útiles, especialmente si se usa algo en tiempo real (como acceder a una base de datos).

¿Demasiadas? Ojo del espectador.

He encontrado que copiar un registro a Word y buscar con "buscar" - si el lector de registro no tiene "buscar" o "buscar" como parte de sus herramientas incluidas - es simple pero excelente forma de avanzar a través de registros detallados.

Ciertamente parece "defensivo" en el sentido ordinario de la palabra.

He encontrado, a través de la experiencia, seguir lo que haga su gerente, líder de equipo o compañero de trabajo. Si solo está programando para usted, utilícelos hasta que el código sea "estable" o en compilaciones de depuración, y luego elimínelos cuando haya terminado.

4

En C++, el único motivo para escribir muchos bloques try/catch es obtener un seguimiento de la pila donde se produjo la excepción. Lo que usted hace es escribir un try/catch en todas partes, y (suponiendo que no se encuentre en el lugar correcto para tratar con la excepción) hacer que el catch capture alguna información de rastreo y luego volver a lanzar la excepción. De esta forma, si una excepción sube todo y hace que el programa finalice, tendrá un rastro de pila completa donde todo comenzó a ir mal (si no lo hace, entonces una excepción de C++ no controlada ha desenrollado útilmente la pila y ha erradicado cualquier posibilidad de que usted averigüe de dónde vino).

Me imagino que en cualquier idioma con un mejor manejo de excepciones (es decir, las excepciones no detectadas le dicen de dónde vienen) querría detectar solo excepciones si pudiera hacer algo al respecto. De lo contrario, solo harás que tu programa sea difícil de leer.

Cuestiones relacionadas