Java: ¿Excepciones como flujo de control?

He oído que usar excepciones para el flujo de control es una mala práctica. ¿Qué piensas de esto?

public static findStringMatch(g0, g1) { int g0Left = -1; int g0Right = -1; int g1Left = -1; int g1Right = -1; //if a match is found, set the above ints to the proper indices //... //if not, the ints remain -1 try { String gL0 = g0.substring(0, g0Left); String gL1 = g1.substring(0, g1Left); String g0match = g0.substring(g0Left, g0Right); String g1match = g1.substring(g1Left, g1Right); String gR0 = g0.substring(g0Right); String gR1 = g1.substring(g1Right); return new StringMatch(gL0, gR0, g0match, g1match, gL1, gR1); } catch (StringIndexOutOfBoundsException e) { return new StringMatch(); //no match found } 

Por lo tanto, si no se ha encontrado ninguna coincidencia, las entradas serán -1. Esto causará una excepción cuando bash tomar la subcadena g0.substring(0, -1) . Luego, la función simplemente devuelve un objeto que indica que no se encuentra ninguna coincidencia.

¿Es esta mala práctica? Podría simplemente verificar cada índice manualmente para ver si son todos -1, pero parece más trabajo.

ACTUALIZAR

He eliminado el bloque try-catch y lo reemplacé con esto:

  if (g0Left == -1 || g0Right == -1 || g1Left == -1 || g1Right == -1) { return new StringMatch(); } 

¿Qué es mejor: verificar si cada variable es -1, o usar un boolean foundMatch para realizar un seguimiento y simplemente verificar eso al final?

En general, las excepciones son operaciones costosas y, como su nombre indica, condiciones excepcionales. Entonces, utilizarlos en el contexto del control del flujo de su aplicación se considera una mala práctica.

Específicamente en el ejemplo que proporcionó, necesitaría hacer una validación básica de las entradas que está proporcionando al constructor StringMatch. Si se tratara de un método que devuelve un código de error en caso de que falle la validación de algunos parámetros básicos, puede evitar consultarlo previamente, pero este no es el caso.

He hecho algunas pruebas sobre esto. En las JVM modernas, en realidad no afecta mucho el rendimiento del tiempo de ejecución (si es que lo hace). Si ejecuta con la depuración activada, entonces desacelera considerablemente las cosas.

Vea lo siguiente para detalles

(También debo mencionar que todavía creo que esta es una mala práctica, incluso si no afecta el rendimiento. Más que nada, refleja un diseño de algoritmo posiblemente deficiente que será difícil de probar)

Sí, esta es una mala práctica, especialmente cuando tiene un medio para evitar una excepción (compruebe la longitud de la cadena antes de intentar indexarla). Los bloques de prueba y captura están diseñados para dividir la lógica “normal” entre lógica “excepcional” y error. En su ejemplo, ha extendido la lógica “normal” al bloque de excepción / error (no encontrar una coincidencia no es excepcional). También está haciendo un uso indebido de la substring para que pueda aprovechar el error que produce como flujo de control.

El flujo del progtwig debe ser lo más recto posible (ya que incluso entonces las aplicaciones se vuelven bastante complejas), y utilizar estructuras de flujo de control estándar. Es posible que el siguiente desarrollador que toque el código no sea usted y (con razón) malinterprete la forma no estándar en la que utiliza excepciones en lugar de condicionales para determinar el flujo de control.

Estoy peleando con un sesgo ligeramente diferente sobre este problema ahora mismo durante la refacturación de código heredado.

El problema más grande que encuentro con este enfoque es que usar el try / catch rompe el flujo programático normal.

En la aplicación en la que estoy trabajando (y esto es diferente de la muestra que ha aplicado), las excepciones se utilizan para comunicarse desde una llamada a un método que ocurrió un resultado determinado (por ejemplo, buscar un número de cuenta y no encontrarlo). Esto crea un código de spaghetti en el lado del cliente, ya que el método de llamada (durante un evento no excepcional, o un evento de caso de uso normal) rompe el código que estaba ejecutando antes de la llamada y en el bloque catch. Esto se repite en algunos métodos muy largos muchas veces, lo que hace que el código sea muy fácil de leer mal.

Para mi situación, un método debería devolver un valor por su firma para todos los eventos excepcionales, excepto para los excepcionales. El mecanismo de manejo de excepciones está destinado a tomar otra ruta cuando se produce la excepción (intente y recupere desde dentro del método para que pueda regresar normalmente).

En mi opinión, podrías hacer esto si ajustas tus bloques de prueba / captura muy estrechamente; pero creo que es un mal hábito y puede conducir a un código que es muy fácil de malinterpretar, ya que el código de llamada interpretará cualquier excepción lanzada como un mensaje de tipo ‘GOTO’ , alterando el flujo del progtwig. Temo que aunque este caso no caiga en esta trampa, hacer esto a menudo podría resultar en un hábito de encoding que conduzca a la pesadilla que estoy viviendo en este momento.

Y esa pesadilla no es agradable.

    Intereting Posts