tipos patterns patrones para lista ejemplos diseño aplicaciones antipatrones antipatron anti c# java c++ exception try-catch-finally

c# - patterns - patrones de diseño para aplicaciones web



¿Es un bloque finalmente sin bloque de captura un antipatrón de Java? (12)

Acabo de tener una experiencia de solución de problemas muy dolorosa en la solución de problemas de un código que se veía así:

try { doSomeStuff() doMore() } finally { doSomeOtherStuff() }

El problema fue difícil de solucionar porque doSomeStuff () arrojó una excepción, que a su vez provocó que doSomeOtherStuff () arrojara una excepción. La segunda excepción (lanzada por el bloque finally) fue arrojada a mi código, pero no tenía un control sobre la primera excepción (lanzada desde doSomeStuff ()), que era la verdadera causa raíz del problema.

Si el código hubiera dicho esto en su lugar, el problema habría sido evidente:

try { doSomeStuff() doMore() } catch (Exception e) { log.error(e); } finally { doSomeOtherStuff() }

Así que mi pregunta es esta:

¿Es un bloque finalmente utilizado sin ningún bloque catch un conocido anti patrón de Java? (Ciertamente, parece ser una subclase no fácilmente aparente del obviamente anti-patrón "¡No tragues excepciones!")


Creo que el verdadero "antipatrón" aquí está haciendo algo en un bloque final que puede arrojar, no sin atrapar.


Creo que está lejos de ser un antipatrón y es algo que hago con mucha frecuencia cuando es crítico desasignar recursos obtenidos durante la ejecución del método.

Una cosa que hago cuando trato con manejadores de archivos (para escribir) es descargar el flujo antes de cerrarlo usando el método IOUtils.closeQuietly, que no arroja excepciones:

OutputStream os = null; OutputStreamWriter wos = null; try { os = new FileOutputStream(...); wos = new OutputStreamWriter(os); // Lots of code wos.flush(); os.flush(); finally { IOUtils.closeQuietly(wos); IOUtils.closeQuietly(os); }

Me gusta hacerlo de esta forma por las siguientes razones:

  • No es completamente seguro ignorar una excepción al cerrar un archivo; si todavía hay bytes que no se escribieron en el archivo, es posible que el archivo no esté en el estado esperado por la persona que llama;
  • Por lo tanto, si se produce una excepción durante el método flush (), se propagará a la persona que llama pero aún así me aseguraré de que todos los archivos estén cerrados. El método IOUtils.closeQuietly (...) es menos detallado que el try correspondiente ... catch ... ignore me block;
  • Si usa múltiples flujos de salida, el orden para el método flush () es importante. Las corrientes que se crearon al pasar otras secuencias como constructores se deben enjuagar primero. Lo mismo es válido para el método close (), pero el flush () es más claro en mi opinión.

Creo que intentar sin trampa es anti-patrón. Usar try / catch para manejar condiciones excepcionales (errores de IO de archivo, tiempo de espera de socket, etc.) no es un antipatrón.

Si está utilizando try / finally para la limpieza, considere usar un bloque en su lugar.


De ningún modo.

Lo que está mal es el código dentro del finalmente.

Recuerda que finalmente siempre se ejecutará, y es arriesgado (como acabas de presenciar) poner algo que pueda arrojar una excepción allí.


En general, no, esto no es un antipatrón. El objetivo de los bloques finalmente es asegurarse de que las cosas se limpien independientemente de si se lanza una excepción o no. El objetivo de la gestión de excepciones es que, si no se puede manejar, se lo permite a alguien que puede, a través de la gestión de excepción de señalización relativamente limpia fuera de banda que proporciona. Si necesita asegurarse de que las cosas se limpian si se lanza una excepción, pero no puede manejar adecuadamente la excepción en el alcance actual, entonces esto es exactamente lo que debe hacer. Es posible que desee ser un poco más cuidadoso para asegurarse de que su bloque finalmente no arroje.


En mi opinión, es más el caso de que finally con una catch indique algún tipo de problema. La expresión de recursos es muy simple:

acquire try { use } finally { release }

En Java, puede tener una excepción desde prácticamente cualquier lugar. A menudo, la adquisición arroja una excepción marcada, la forma sensata de manejar eso es poner un poco de todo el lote. No intentes alguna horrible comprobación nula.

Si fueras realmente anal, deberías notar que hay prioridades implícitas entre las excepciones. Por ejemplo, ThreadDeath debería destruir todo, ya sea que se trate de adquirir / usar / liberar. Manejar estas prioridades correctamente es desagradable.

Por lo tanto, abstraiga su manejo de recursos con el modismo Execute Around.


No hay absolutamente nada de malo en probar con un final y sin atrapar. Considera lo siguiente:

InputStream in = null; try { in = new FileInputStream("file.txt"); // Do something that causes an IOException to be thrown } finally { if (in != null) { try { in.close(); } catch (IOException e) { // Nothing we can do. } } }

Si se lanza una excepción y este código no sabe cómo manejarlo, entonces la excepción debe llenar la pila de llamadas para la persona que llama. En este caso, todavía queremos limpiar la secuencia, así que creo que tiene sentido tener un bloque de prueba sin trampas.


Try / Finally es una forma de liberar adecuadamente los recursos. El código en el bloque finally NUNCA debe arrojarse, ya que solo debe actuar sobre los recursos o el estado que se adquirió ANTES de la entrada en el bloque try.

Como un aparte, creo que log4J es casi un antipatrón.

SI DESEA INSPECCIONAR UN PROGRAMA EN EJECUCIÓN, USE UNA HERRAMIENTA DE INSPECCIÓN CORRECTA (es decir, un depurador, IDE o, en un sentido extremo, un tejedor de código de bytes, pero NO PONGA LAS DECLARACIONES DE REGISTRO EN CIERTAS LÍNEAS).

En los dos ejemplos que presenta, el primero parece correcto. El segundo incluye el código del registrador e introduce un error. En el segundo ejemplo, suprime una excepción si las dos primeras afirmaciones arrojan una (es decir, la capta y la registra, pero no la vuelve a lanzar. Esto es algo que encuentro muy común en el uso de log4j y es un problema real del diseño de la aplicación. con su cambio puede hacer que el programa falle de una manera que sería muy difícil de manejar para el sistema, ya que básicamente marcha hacia adelante como si nunca hubiera tenido una excepción (es decir, como VB básico en caso de error reanudar el próximo constructo).


Utilizo try / finally en la siguiente forma:

try{ Connection connection = ConnectionManager.openConnection(); try{ //work with the connection; }finally{ if(connection != null){ connection.close(); } } }catch(ConnectionException connectionException){ //handle connection exception; }

Prefiero esto al try / catch / finally (+ try / catch anidado al final). Creo que es más conciso y no duplico la captura (Excepción).


Yo diría que un bloque de prueba sin un bloque catch es un antipatrón. Decir "No tener un finalmente sin una captura" es un subconjunto de "No lo intentes sin atrapar".


try-finally puede ayudarlo a reducir el código de copiar y pegar en caso de que un método tenga múltiples declaraciones de return . Considere el siguiente ejemplo (Android Java):

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) { Cursor cursor = db.rawQuery("SELECT * FROM table", null); if (cursor != null) { try { if (cursor.getCount() == 0) { return false; } } finally { // this will get executed even if return was executed above cursor.close(); } } // database had rows, so do something... return true; }

Si no hubo una cláusula finally , es posible que cursor.close() escribir cursor.close() dos veces: justo antes de return false y también después de la cláusula if circundante.


try { doSomeStuff() doMore() } catch (Exception e) { log.error(e); } finally { doSomeOtherStuff() }

No hagas eso tampoco ... acabas de esconder más errores (bueno, no exactamente los escondiste ... pero ha hecho más difícil lidiar con ellos. Cuando capturas Excepción, también estás atrapando cualquier tipo de RuntimeException (como NullPointer y ArrayIndexOutOfBounds) .

En general, capte las excepciones que tiene que atrapar (excepciones marcadas) y trate con los demás en el momento de la prueba. RuntimeExceptions está diseñado para ser utilizado para errores del programador, y los errores del programador son cosas que no deberían ocurrir en un programa correctamente depurado.