È una cattiva pratica avere delle funzioni il cui unico scopo è gettare errori?

7

Ho la seguente situazione.

Sto scrivendo un programma c ++ in cui ho una funzione che controlla un'equivalenza personalizzata di due oggetti json. Se il controllo fallisce, non voglio che l'intero programma si fermi (è un programma Qi) e ho un ramo che si esegue dopo aver ricevuto un'eccezione. Questa eccezione viene quindi passata a QT dove un qt gui visualizza un messaggio di errore da e.what() .

Per fare ciò, ho creato una funzione che si è occupata della mia eccezione durante il lancio dei confronti di equivalenza. sembra qualcosa del genere:

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

Ho discusso solo di rendere questa una funzione booleana, tuttavia volevo dei messaggi di errore dettagliati per dire esattamente cosa è andato storto nel programma all'utente. Sembra strano avere una funzione che non faccia altro che generare errori nei confronti dei guasti, e quindi doverli intercettare e continuare a lanciarli verso l'alto. Mi sento come se ci fosse un modo migliore per gestire questo. Ecco ME su come si presenta il resto del programma.

// 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());
}
    
posta opa 17.05.2017 - 20:13
fonte

2 risposte

3

Nel tuo esempio, le tue procedure di confronto e gestione si presentano molto vicine tra loro. Di solito, l'eccezione di tiro / presa è destinata a gestire la situazione inversa, dove le cose sono lontane. Non hai davvero bisogno di lanciare l'eccezione da makeComparison() , puoi restituire un valore Cause . Se Cause è OK , procedi con il tuo percorso felice, altrimenti puoi attivare Cause per segnalare e recuperare, quindi lanciarlo sulla GUI.

Questo fa uscire "l'eccezione come controllo del flusso" dall'immagine, finché non è necessario dai requisiti di messaggistica di Qt.

Modifica: a proposito, Cause è semplicemente un modo per incapsulare un codice di ritorno in modo sensato. In alcuni ambienti, creo una sottoclasse Throwable per conservare le informazioni sullo stato, in modo che io possa effettivamente throw quando necessario.

    
risposta data 17.05.2017 - 20:35
fonte
3

Is it bad practice to have functions whose sole purpose is to throw errors?

Per me il metodo makeComparision() sta implementando le eccezioni per il controllo del flusso e questo è considerato anti-pattern da molti. Ma, come al solito, l'idoneità nell'ingegneria del software è legata ai bisogni e ai requisiti che variano tra i progetti

A parte gli anti-schemi, l'implementazione solleva altre preoccupazioni, vorrei evidenziare

Semanticamente fuorviante

Dal punto di vista di uno sviluppatore, un blocco di codice la cui principale responsabilità è cercare discrepanze nello stato dei dati e generare errori è solitamente considerato un validatore piuttosto che un comparatore .

Leggibilità dubbia

A causa del nome fuorviante e della firma effettiva, siamo costretti a guardare all'interno del metodo per comprenderne la funzione. Dai un'occhiata a Principio di minimo stupore , potrebbe interessarti.

Inoltre, la tracciabilità coinvolge il salto da una classe all'altra. La sequenza di esecuzione diventa sfocata.

Complessità

Dal punto di vista della manutenibilità, un metodo composto fondamentalmente da blocchi if/else tende ad aggiungere complessità ciclomatica. Contribuisce anche alla leggibilità dubbia.

Genera debito tecnico che è spesso un segno di un design scadente. A lungo andare, il debito tecnico diventa un problema reale che spesso limita il modo in cui il sistema si evolve.

Le proposte

Considera i prossimi suggerimenti

Restituzione di una risposta valida

Permettere di sapere cosa è successo durante il confronto in modo aggraziato potrebbe essere un grande miglioramento del design. Affronta anche alcune delle preoccupazioni sopra menzionate.

Un possibile candidato ( ha scritto in 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 []{});
    }
}

Separazione di conocern

Un comparatore non ha necessariamente bisogno di sapere che diversi JSON causano errori. Quindi, perché non deleghiamo la convalida dei risultati a un altro componente ?. Ad esempio, il makeComparison() chiamante. Riduce la complessità del metodo, migliora la leggibilità e la manutenibilità.

Se queste modifiche non sono possibili, prendi in considerazione la possibilità di cambiare il nome in validationByComparision() . Ha più senso di conseguenza con la sua effettiva implementazione.

    
risposta data 17.05.2017 - 23:48
fonte

Leggi altre domande sui tag