2010-03-15 38 views
8

Escribí una clase que carga los objetos de configuración de mi aplicación y los hace un seguimiento para que pueda escribir fácilmente cambios o volver a cargar toda la configuración a la vez con una sola llamada a un método. Sin embargo, cada objeto de configuración podría arrojar una excepción al hacer IO, pero no quiero que esos errores cancelen el proceso general para dar a los demás objetos la posibilidad de volver a cargar/escribir. Por lo tanto, recopilo todas las excepciones que se lanzan al iterar sobre los objetos y almacenarlos en una super-excepción, que se lanza después del bucle, ya que cada excepción debe ser manejada y alguien debe ser notificado de qué fue exactamente lo que salió mal. Sin embargo, ese enfoque me parece un poco extraño. Alguien por ahí con una solución más limpia?Manejo de múltiples excepciones

Aquí hay un código de la clase mencionada:

public synchronized void store() throws MultipleCauseException 
    { 
    MultipleCauseException me = new MultipleCauseException("unable to store some resources"); 
    for(Resource resource : this.resources.values()) 
     { 
     try 
      { 
      resource.store(); 
      } 
     catch(StoreException e) 
      { 
      me.addCause(e); 
      } 
     } 
    if(me.hasCauses()) 
     throw me; 
    } 

Respuesta

4

Si desea mantener los resultados de las operaciones, los que parece que haces en el desempeño propósito, entonces lanzar una excepción es la cosa incorrecta que hacer. Por lo general, debes intentar no molestar nada si lanzas una excepción.

Lo que sugiero es pasar las excepciones, o los datos derivados de ellas, a un error al manejar la devolución de llamada a medida que avanza.

public interface StoreExceptionHandler { 
    void handle(StoreException exc); 
} 

public synchronized void store(StoreExceptionHandler excHandler) { 
    for (Resource resource : this.resources.values()) { 
     try { 
      resource.store(); 
     } catch (StoreException exc) { 
      excHandler.handle(exc); 
     } 
    } 
    /* ... return normally ... */ 
] 
+0

Considerando la recarga, los objetos que emiten una excepción representan el último estado consistente posible (es decir, mantienen una copia obsoleta de la configuración). –

3

Hay principios rectores en el diseño de lo que y cuando las excepciones deben ser lanzados, y los dos más relevantes para este escenario son:

  • excepciones lanzamiento correspondiente a la abstracción (es decir, el paradigma traducción excepción)
  • excepciones Throw temprano si es posible

La forma de traducir StoreException-MultipleCauseException sE Es razonable para mí, aunque agrupar diferentes tipos de excepciones en uno puede no ser la mejor idea. Desafortunadamente, Java no es compatible con los genéricos Throwable s, por lo que quizás la única alternativa sea crear una subclase MultipleStoreException por separado.

Con respecto a tirar excepciones lo más temprano posible (que NO estás haciendo), diré que está bien doblar la regla en ciertos casos. Siento que el peligro de retrasar un lanzamiento es cuando las situaciones excepcionales se anidan en una reacción en cadena innecesariamente. Siempre que sea posible, debe evitar esto y localizar la excepción al alcance más pequeño posible.

En su caso, si tiene sentido pensar conceptualmente en el almacenamiento de recursos como tareas independientes múltiples, entonces puede estar bien "procesar por lotes" la excepción de la forma en que lo hizo. Sin embargo, en otras situaciones en las que las tareas tienen una relación de interdependencia más complicada, agruparlas por completo hará que la tarea de analizar las excepciones sea más difícil.

En un sentido más abstracto, en términos de teoría de grafos, creo que está bien fusionar un nodo con múltiples hijos sin hijos en uno. Probablemente no esté bien fusionar un subárbol grande o, peor aún, un gráfico cíclico, en un nodo.

+0

No se pueden genéricos las excepciones en Java. –

+0

Guau, eso es una sorpresa. Gracias por señalar eso. – polygenelubricants

+0

Ya lo probé y me sorprendió por igual. Sin embargo, no veo ninguna razón, ya que los genéricos simplemente se expanden en algunos modelos implícitos que serían realmente útiles aquí.Tal vez alguien pensó "tenemos borrado de tipo y algo así como catch (GenericException e) {} catch (GenericException e) {} no funcionará, por lo que impedimos que los programadores lo intenten" –