¿Es una mala práctica tener funciones cuyo único propósito es generar errores?

7

Tengo la siguiente situación.

Estoy escribiendo un programa c ++ donde tengo una función que verifica una equivalencia personalizada de dos objetos json. Si la verificación falla, no quiero que se detenga todo el programa (es un programa QT gui) y tengo una rama que se ejecuta al recibir una excepción. Esta excepción se pasa luego a QT, donde la interfaz gráfica qt muestra un mensaje de error de e.what() .

Para hacer esto, hice una función que se encargó de mi excepción de lanzamiento para las comparaciones de equivalencia. se ve algo como esto:

void makeComparison(const json& a, const json& b){
     if(condition fails)
        throw (error)
     if(other condition fails)
        throw (error)
     ...
}

Debatí simplemente hacer de esto una función booleana en su lugar, sin embargo, quería mensajes de error detallados para decir exactamente qué fue lo que salió mal en el programa para el usuario. Parece extraño tener una función que no haga nada más que lanzar errores en fallas de comparación, y luego tener que atraparlos y seguir lanzándolos hacia arriba. Siento que tiene que haber una mejor manera de manejar esto. Aquí está ME de lo que parece el resto del programa.

// some member function
try{
    makeComparison(a, m_b);
    // do something
}catch (exception e){
   // do soemthing else
   throw(e);
}
// in gui
try{
    object.exceptionThrowingCode(a);
}catch (exception e){
   QMessageBox::critical(this, tr("Error"), tr(e.what());
}
    
pregunta opa 17.05.2017 - 20:13

2 respuestas

3

En su ejemplo, sus procedimientos de comparación y manejo ocurren muy cerca uno del otro. Por lo general, el lanzamiento / captura de excepción está destinado a manejar la situación inversa, donde las cosas están muy lejos. Realmente no es necesario lanzar la excepción desde makeComparison() , puede devolver un valor Cause . Si el Cause es OK , continúe con su ruta feliz; de lo contrario, puede activar Cause para informar y recuperar, y luego enviarlo a la GUI.

Esto elimina la "excepción como control de flujo" de la imagen, hasta que sea necesaria por los requisitos de mensajería de Qt.

Editar: Por cierto, Cause es simplemente una forma de encapsular un código de retorno de una manera sensible. En algunos entornos, creo una subclase Throwable para guardar información de estado, de modo que pueda realmente throw cuando sea necesario.

    
respondido por el BobDalgleish 17.05.2017 - 20:35
3
  

Es una mala práctica tener funciones cuyo único propósito es lanzar   errores?

Para mí, el método makeComparision() está implementando excepciones para el flujo de control, y eso se considera antipatrón por mucho. Pero, como es habitual, la idoneidad en la ingeniería del software está vinculada a las necesidades y requisitos que varían entre los proyectos.

Aparte de los patrones, la implementación plantea otras inquietudes, me gustaría resaltar

Semánticamente engañoso

Desde el punto de vista de un desarrollador, un bloque de código cuya responsabilidad principal es buscar discrepancias en el estado de los datos y generar errores generalmente se considera un validador en lugar de un comparador .

legibilidad dudosa

Debido al nombre engañoso y la firma real, nos vemos obligados a mirar dentro del método para comprender su función. Eche un vistazo al Principio de menos asombro , Le puede interesar.

Además, la trazabilidad implica saltos de una clase a otra. La secuencia de ejecución se vuelve borrosa.

Complejidad

Desde el punto de vista de la capacidad de mantenimiento, un método compuesto básicamente por bloques if/else tiende a agregar complejidad ciclomática. También contribuye a la legibilidad dudosa.

Genera deuda técnica que a menudo es un signo de un diseño deficiente. A largo plazo, la deuda técnica se convierte en un problema real que a menudo restringe la forma en que evoluciona el sistema.

Propuestas

Considera las siguientes sugerencias

Devolviendo una respuesta válida

Permitir saber lo que sucedió durante la comparación de una manera elegante podría ser una gran mejora en el diseño. También aborda algunas de las preocupaciones mencionadas anteriormente.

Un posible candidato ( escribió en java ):

public class ComparisionReport {

    private Collection<String> differences;

    public ComparisionReport(Collection<String> differences){
        this.differences = differences;
    }

    public boolean areDifferents(){
        return !this.differences.isEmtpy();
    }

    public boolean areEquals(){
        return this.differences.isEmtpy();
    }

    public String [] getDifferences(){
        return this.differences.toArray(new String []{});
    }
}

Separación de conocerns

Un comparador no necesariamente necesita saber que diferentes JSON causan errores. Entonces, ¿por qué no delegamos la validación de resultados a otro componente? Por ejemplo, la persona que llama makeComparison() . Reduce la complejidad del método, mejora la legibilidad y la capacidad de mantenimiento.

Si estos cambios no son posibles, considere cambiar el nombre a validationByComparision() . Tiene más sentido en consecuencia con su implementación real.

    
respondido por el Laiv 17.05.2017 - 23:48

Lea otras preguntas en las etiquetas