tipos propagacion paquetes manejo lanzamiento jerarquia excepciones ejemplos arbol java exception visitor

paquetes - propagacion de excepciones en java



Cómo usar excepciones marcadas en un patrón de visitante (4)

Iba a mencionar el enfoque de re-lanzamiento envuelto sin marcar, pero Giodude me dio una paliza. En su lugar, sugeriré otro enfoque al que llamo la excepción de cortesía (porque está integrado en las interfaces como una cortesía para los implementadores).

Al diseñar el visitante y las interfaces de mamíferos, los equipo para manejar una excepción de la elección del usuario. El visitante:

public interface MammalVisitor<T extends Throwable> { void visit(Cat m) throws T; void visit(Dog m) throws T; void visit(Cow m) throws T; }

Y mamífero

public interface Mammal { <T extends Throwable> void accept(MammalVisitor<T> visitor) throws T; }

Y la implementación de Mamíferos:

public class Cat implements Mammal { @Override public <T extends Throwable> void accept(MammalVisitor<T> visitor) throws T { visitor.visit(this); } }

Perro y vaca se implementan de manera idéntica. Y el visitante de impresión:

public class MammalPrinter implements MammalVisitor<IOException> { private final Appendable out; public MammalPrinter(Appendable out) { this.out = out; } @Override public void visit(Cat m) throws IOException { out.append("I''m a cat"); } @Override public void visit(Dog m) throws IOException { out.append("I''m a dog"); } @Override public void visit(Cow m) throws IOException { out.append("I''m a cow"); } }

Y uso:

Mammal m = MammalFactory.getMammal(); MammalPrinter mp = new MammalPrinter(System.out); try { m.accept(mp); } catch (IOException e) { System.err.println("An IOException occurred"); }

Lo que resulta en un uso mucho más intuitivo y fácil de implementar desde la perspectiva del usuario final.

Con este patrón, si un visitante no tiene una excepción marcada para lanzar, especifican una excepción no marcada como genérica en su implementación:

public class MammalPrinter implements MammalVisitor<RuntimeException> {

Cuando se llama a Mammal.accept () con el visitante anterior, no se necesita captura para ser sintácticamente correcto. Quizás podría aumentar aún más la legibilidad haciendo una extensión de RuntimeException llamada "NeverThrown" que tiene un constructor privado.

Supongamos que tengo un conjunto de clases que aceptan un visitante (patrón de visitante), pero debido a la naturaleza de esas clases o de un visitante en particular, el hecho de trabajar en ellas probablemente sea capaz de lanzar una excepción comprobada.

La interfaz de aceptación de visitantes:

public interface Mammal { void accept(MammalVisitor visitor); }

La interfaz de visitante:

public interface MammalVisitor { void visit(Cat m); void visit(Dog m); void visit(Cow m); }

Y las implementaciones de Mammal:

public class Cat implements Mammal { public void accept(MammalVisitor visitor) { visitor.visit(this); } }

Asumiremos que Dog & Cow se implementan de forma idéntica a Cat.

Ahora supongamos que mi visitante es:

public class MammalPrinter implements MammalVisitor { private final Appendable out; public MammalPrinter(Appendable out) { this.out = out; } @Override public void visit(Cat m) { out.append("I''m a cat"); } @Override public void visit(Dog m) { out.append("I''m a dog"); } @Override public void visit(Cow m) { out.append("I''m a cow"); } }

E imprimo el resultado a stdio:

Mammal m = MammalFactory.getMammal(); MammalPrinter mp = new MammalPrinter(System.out); m.accept(mp);

Sin embargo, MammalPrinter anterior es sintácticamente incorrecto , porque Appendable.append (String) lanza la excepción java.io.IOException No puedo declarar la tirada en cada método de visita, porque no se declara en la interfaz del visitante.

Las soluciones que he considerado:

  • Declare throws IOException en Mammal.accept() , los tres MammalVisitor.visit() y los tres MammalPrinter.visit()
    • Muy desagradable: las interfaces Mammal y MammalVisitor ahora son conscientes de su uso potencial que implica IO, lo que es contrario al punto en el que se usa el patrón de visitante.
  • Declare MammalVisitor.visit() on Mammal.accept() y los tres MammalVisitor.visit() , y declara throws IOException en los tres MammalPrinter.visit()
    • Mejor que la solución anterior: Mammal y MammalVisitor ahora son de uso agnóstico . Sin embargo, ahora también son difíciles de usar: los visitantes que no emiten excepciones todavía están obligados a manejar Throwable desde el método accept ().

Tengo otras dos soluciones que prefiero sobre las anteriores, con las cuales responderé mi publicación. Me gustaría ver cuál es el favorito de la comunidad en general.


Le falta una opción que es manejar internamente la excepción IOException. Las opciones son ignorarlo, reportarlo o volver a envolverlo como una excepción RuntimeException ...

// Ignore it (swallow it). @Override public void visit(Cow m) { try { out.append("I''m a cow"); } catch (IOException ioe) { // swallow this exception - it will never happen } } // report it @Override public void visit(Cow m) { try { out.append("I''m a cow"); } catch (IOException ioe) { ioe.printStackTrace(); } } // wrap and rethrow it. @Override public void visit(Cow m) { try { out.append("I''m a cow"); } catch (IOException ioe) { throw new IllegalStateException("Unable to append to output", ioe); } }


Puede capturar las excepciones marcadas y lanzarlas envueltas en excepciones no verificadas. Vea, por ejemplo, cómo Spring convierte JDBC o JMS comprobó las excepciones a las no verificadas.


Supongo que depende de si desea que su código de cliente se vea afectado por esas excepciones o no. Podría tragarlos dentro del código, envuélvalos en Excepciones de tiempo de ejecución como se mencionó anteriormente, o alternativamente, podría agregar Excepciones a los métodos de ''visita'' y hacer algo con él o volver a hacerlo con el método de aceptación.

Hacer la última opción sería reconocer que todo lo que haga el visitante tiene la capacidad de crear una condición de excepción y que el código de llamada debe ser consciente de ello.