design - programacion - manejar excepciones en java
¿Cuáles son los principios que guían su política de manejo de excepciones? (12)
Hay mucha relatividad involucrada en trabajar con excepciones. Más allá de las API de bajo nivel donde las excepciones cubren los errores surgidos del hardware y el sistema operativo, hay un área sombreada donde el programador decide qué constituye una excepción y cuál es una condición normal.
¿Cómo decides cuándo usar excepciones? ¿Tiene una política coherente con respecto a las excepciones?
Nunca arrojes excepciones de los destructores.
Mantenga un nivel básico de garantías de excepción sobre el estado del objeto.
No use excepciones para comunicar errores que se pueden hacer usando un código de error a menos que sea un verdadero error de excepción y es posible que desee que las capas superiores lo sepan.
No arrojes excepciones si puedes evitarlo. Acelera todo.
No solo
catch(...)
y no haga nada. Captura excepciones que conoces o excepciones específicas. Por lo menos, registra lo que sucedió.Cuando en el mundo de excepción usa RAII porque ya nada es seguro.
El código de envío no debería haber suprimido las excepciones, al menos en lo que respecta a la memoria.
Al lanzar excepciones, empaquete toda la información posible junto con ella para que las capas superiores tengan suficiente información para depurarlas.
Conozca las banderas que pueden hacer que bibliotecas como STL generen excepciones en lugar de exhibir un comportamiento desconocido (p. Ej., Iteración no válida / desbordamiento de subíndice vectorial).
¿Captura referencias en lugar de copias del objeto de excepción?
Tenga especial cuidado con los objetos contados de referencia como COM y combadándolos en punteros contados de referencia cuando trabaje con código que podría arrojar excepciones.
Si un código arroja una excepción más del 2% del tiempo, considere la posibilidad de convertirlo en un código de error por el rendimiento.
Considere la posibilidad de no lanzar excepciones de las exportaciones / interfaces C sin decorar dll porque algunos compiladores optimizan al asumir el código C para no lanzar excepciones.
Si todo lo que hace por manejar excepciones es algo similar a lo que se ve a continuación, entonces no use el manejo de excepciones. No lo necesitas.
main { try { all code.... } catch(...) {} }
¿No son excepciones planteadas por el entorno de idioma de acuerdo con la especificación. de la lengua que se utiliza si de hecho tiene el concepto de excepciones? Estoy pensando en "dividir por cero" en Java, o CONSTRAINT_ERROR en Ada versus nada en absoluto en C.
¿Cómo puede un programador "decidir" usar excepciones después de seleccionar un lenguaje de programación que tiene excepciones definidas dentro de su composición?
Editar: O en lugar de "usar" excepciones, ¿quiere decir cuándo tener una política cohesiva y coherente sobre las excepciones de "manejo"?
Edit2: le gustaría consultar el capítulo gratuito del libro de Steven Dewhurst "C ++ Gotchas", específicamente Gotcha 64 y Gotcha 65. Aunque se centra en C ++, las lecciones involucradas son útiles en otros idiomas.
Como desarrollador de C ++, mi propia política es no arrojar excepciones de lo que considero apis público a mis clases / módulos (de hecho, un requisito con COM). Sin embargo, utilizo excepciones ampliamente en la implementación de clases privadas. Por ejemplo, trabajando con ATL:
HRESULT Foo()
{
HRESULT hr = S_OK;
try {
// Avoid a whole lot of nested ifs and return code
// checking - internal stuff just throws.
DoStuff();
DoMoreStuff(); // etc.
} catch ( CAtlException& e ) {
hr = e;
}
return hr;
}
void DoSomething()
{
// If something goes wrong, AtlThrow( E_FAILED or E_WHATEVER );
}
Creo que generalmente hay una buena manera de determinar excepciones en función del acceso a los recursos, la integridad de los datos y la validez de los datos.
Excepciones de acceso
- Crear o conectarse a cualquier tipo de conexión (remota, local).
- Ocurre en: Bases de datos, Remoting
- Razones: inexistente, ya en uso o no disponible, credenciales insuficientes / inválidas
- Abrir, leer o escribir en cualquier tipo de recurso
- Ocurre en: archivo de E / S, base de datos
- Razones: credenciales bloqueadas, no disponibles, insuficientes / inválidas
Integridad de los datos
- Podría haber muchos casos donde la integridad de los datos importa
- A qué se refiere, qué contiene ...
- Busque recursos en los métodos o códigos que requieren un conjunto de criterios para que los datos estén limpios y en un formato válido.
- Ejemplo: intentar analizar una cadena con el valor ''bleh'' en un número.
Validez de los datos
- ¿Esta es la información correcta provista? (Está en el formato correcto, pero puede no ser el conjunto correcto de parámetros para una situación dada)
- Ocurre en: consultas de bases de datos, transacciones, servicios web
- Ejemplo: enviar una fila a una base de datos y violar una restricción
Obviamente hay otros casos, pero estos son generalmente los que trato de cumplir cuando es necesario.
Creo que la mejor manera de usar excepciones depende del idioma de la computadora que está utilizando. Por ejemplo, Java tiene una implementación de excepciones mucho más sólida que C ++.
Si está utilizando C ++, le recomiendo que al menos intente leer lo que Bjarne Stroustrup (el inventor de C ++) tiene que decir sobre la seguridad de las excepciones. Consulte el apéndice E de su libro "El lenguaje de programación C ++".
Pasa 34 páginas tratando de explicar cómo trabajar con excepciones de manera segura. Si entiendes su consejo, eso debería ser todo lo que necesitas saber.
El contexto en el que se da esta respuesta es el lenguaje Java.
Para los errores normales que pueden surgir, los manejamos directamente (como regresar inmediatamente si algo es nulo, vacío, etc.). Solo usamos una excepción real para situaciones excepcionales.
Sin embargo, no arrojamos excepciones comprobadas, nunca. Subclasificamos RuntimeException para nuestras propias excepciones específicas, tomándolas cuando corresponda directamente, y en cuanto a las excepciones lanzadas por otras bibliotecas, la API de JDK, etc., intentamos / capturamos internamente y registramos la excepción (si sucedió algo que realmente no debería) ''t tiene y no tiene forma de recuperarse como una excepción de archivo no encontrado para un trabajo por lotes) o envolvemos la excepción en una RuntimeException y luego la lanzamos. En el exterior del código, confiamos en un manejador de excepciones para eventualmente atrapar esa RuntimeException, ya sea la JVM o el contenedor web.
La razón por la que esto se hace es que evita la creación de bloques de prueba / captura forzados en todas partes donde puede tener cuatro instancias para llamar a un método, pero solo uno puede manejar la excepción. Esta parece ser la regla, no la excepción (no hay intención de puntería ... ouch), por lo que si esa cuarta persona puede manejarla, aún puede detectarla y examinar la causa raíz de la excepción para obtener la excepción real que ocurrió ( sin preocuparse por el contenedor RuntimeException).
Esta entrada de blog de Eric Lippert, Ingeniero Senior de Diseño de Software en Microsoft, resume un excelente y breve conjunto de pautas de estrategia de excepción .
En breve:
Fatal : errores terribles que indican que su proceso es totalmente irrecuperable. Limpia todos los recursos que puedas, pero no los atrapes. Si está escribiendo código que tiene la capacidad de detectar dicha situación, por supuesto, tire. Ejemplo: excepción de falta de memoria.
Boneheaded : errores relativamente simples que indican que su proceso no puede operar con los datos que se le entregan, pero continuarían normalmente si cualquier situación que causó el error simplemente se ignora. Estos son mejor conocidos como errores. No los arroje ni los atrape, sino que evite que sucedan, generalmente al pasar errores u otros indicadores significativos de falla que puedan ser manejados por sus métodos. Ejemplo: excepción de argumento nulo.
Hostigamiento : los errores relativamente simples que el código que usted no posee le están tirando. Debe atrapar todo esto y tratar con ellos, por lo general de la misma manera en que trataría con una excepción propia Boneheaded . Por favor, no los tires de vuelta otra vez. Ejemplo: excepción de formato del método Int32.Parse() de C #
Exógenos : errores relativamente sencillos que se parecen mucho a las situaciones Vexing (del código de otras personas) o incluso Boneheaded (de su código), pero deben ser lanzados porque la realidad dicta que el código que los arroja realmente no tiene idea de cómo recuperarse, pero el la persona que llama probablemente lo hará. Adelante, tíralos, pero cuando tu código los reciba de otra parte, atrápalos y ocúpate de ellos. Ejemplo: excepción de archivo no encontrado.
De los cuatro, los exógenos son en los que tienes que pensar más para hacerlo bien. Una excepción que indica que no se encuentra un archivo es apropiado para un método de biblioteca de E / S, ya que es casi seguro que el método no sepa qué hacer si no se encuentra el archivo, especialmente dado que la situación puede ocurrir en cualquier momento y que no es forma de detectar si la situación es transitoria o no. Sin embargo, lanzar una excepción de este tipo no sería apropiado para el código de nivel de aplicación, porque esa aplicación puede obtener información del usuario sobre cómo proceder.
Las excepciones no deben usarse como un método para pasar información internamente entre los métodos dentro de su objeto, localmente debe usar códigos de error y programación defensiva.
Las excepciones están diseñadas para pasar el control desde un punto donde se detecta un error a un lugar (más arriba en la pila) donde se puede manejar el error, presumiblemente porque el código local no tiene suficiente contexto para corregir el problema y algo más arriba en la pila tendrá más contexto y así podrá organizar mejor una recuperación.
Al considerar excepciones (al menos en C ++), debe considerar las garantías de excepción que su API hace. El nivel mínimo de garantía debe ser la garantía básica, aunque debe esforzarse (cuando corresponda) para proporcionar la garantía sólida. En los casos en que no utilice dependencias externas de una API articular, incluso puede tratar de proporcionar la garantía de no lanzamiento.
NB No confunda las garantías de excepción con las especificaciones de excepción.
Garantías de excepción:
Sin garantía:
No hay garantía sobre el estado del objeto después de que una excepción escapa de un método. En estas situaciones, el objeto ya no debe ser utilizado.
Garantía Básica:
En casi todas las situaciones, esta debería ser la garantía mínima que proporciona un método. Esto garantiza que el estado del objeto está bien definido y aún se puede usar de forma consistente.
Garantía fuerte: (también conocida como Garantía transaccional)
Esto garantiza que el método será completamente exitoso O se lanzará una Excepción y el estado de los objetos no cambiará.
Sin garantía de tiro:
El método garantiza que no se permite la propagación de excepciones del método. Todos los destructores deberían hacer esta garantía.
| NB Si una excepción se escapa de un destructor mientras una excepción ya se está propagando
| la aplicación terminará
Las excepciones son costosas en el tiempo de procesamiento, por lo que solo deben lanzarse cuando sucede algo que realmente no debería suceder en su aplicación.
A veces puede predecir qué tipo de cosas podrían suceder y qué código recuperar de ellas, en cuyo caso es apropiado arrojar y atrapar una excepción, registrar y recuperar, luego continuar. De lo contrario, solo se deberían usar para manejar lo inesperado y salir con gracia, mientras se captura tanta información como sea posible para ayudar con la depuración.
Soy un desarrollador de .NET, y para catch and throw, mi enfoque es:
- Solo intente / capture en métodos públicos (en general, obviamente, si atrapa un error específico, lo buscará allí)
- Solo inicie sesión en la capa de la interfaz de usuario justo antes de suprimir el error y redirigir a una página / formulario de error.
Mi política sobre el manejo de excepciones se puede encontrar en:
http://henko.net/imperfection/exception-handling-policy-throwing-exception/ .
(Espero que no esté en contra de las reglas promover un sitio web, pero es demasiada información para pegar aquí).
Otros pueden tener que corregir / aclarar esto, pero hay una estrategia llamada (creo) "desarrollo impulsado por contrato", donde documenta explícitamente en su interfaz pública cuáles son las condiciones previas esperadas para cada método y las condiciones posteriores garantizadas. Luego, cuando implemente el método, cualquier error que le impida cumplir con las condiciones posteriores en el contrato debería resultar en una excepción lanzada. El incumplimiento de las condiciones previas se considera un error del programa y debe provocar la interrupción del programa.
No estoy seguro de si el desarrollo impulsado por contrato se refiere a la cuestión de la captura de excepciones, pero en general solo debe detectar las excepciones que espera y de las que puede recuperarse razonablemente. Por ejemplo, la mayoría de los códigos no se pueden recuperar significativamente de una excepción de falta de memoria, por lo que no tiene sentido capturarlos. Por otro lado, si está intentando abrir un archivo para escribir, puede (y debe) manejar el caso de que el archivo esté bloqueado exclusivamente por otro proceso, o el caso de que el archivo haya sido eliminado (incluso si lo ha marcado) existencia antes de tratar de abrirlo).
Como señaló otro comentador, también debe evitar el uso de excepciones para manejar las condiciones esperadas que pueden esperarse y evitarse. Por ejemplo, en .NET framework, int.TryParse es preferible a int.Parse con try / catch, especialmente cuando se usa en un bucle o similar.
este artículo de bea (ahora oráculo) es una buena exposición sobre cómo hacerlo: http://www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html . Supone un poco Java, pero también debería poder usarlo para otros entornos.