Il gestore onError è migliore delle eccezioni?

7

Comprendo che un'eccezione nel programma significa che è accaduto qualcosa di imprevedibile (ma non così grave da bloccare inevitabilmente l'applicazione!). La sequenza try-catch-finally mi rende triste perché il programma è più difficile da leggere (un livello in più di parentesi graffe) e più difficile da capire (salta da qualsiasi posizione in caso di eccezione, GOTO è deprecato).

Dato che abbiamo OOP, suggerisco di creare una classe proxy che, in caso di eccezione, la consumi in modo silenzioso, restituisce un valore predefinito e attiva un evento onError. Quindi, se propagiamo un'eccezione, abbiamo invece una chiamata onError e i due svantaggi sopra menzionati sono risolti. Guarda l'esempio in C #:

Atteggiamento delle eccezioni standard

  class Computer {
    // let's say we need to propagate exception and decide what to do lately
    public int divide(int a, int b) {
      int result = a / b;
      return result;
    }
  }

  class Program {
    public static void Main() {
      Computer c = new Computer();
      try {
        Console.WriteLine(c.divide(1, 0));
      }
      catch(ArithmeticException e) {
        // we are teleported here from the middle of Computer.divide method!
        // do something
      }
    }
  } // three levels of brackets (without namespace) in such trivial example??

Atteggiamento del gestore onError

  class ProxyComputer {
    private Computer c = new Computer();
    // it is not virtual, can not be overriden
    public int divide(int a, int b) {
      // alas, exceptions are standard, but we can stop them in this class
      try {
        return c.divide(a, b);
      }
      catch(ArithmeticException e) {
        this.onError(e);
      }
    }

    protected virtual void onError(Exception e) {
      // do nothing
    }
  }

  class MoreStrictComputer : ProxyComputer {
    protected override void onError(Exception e) {
      // mail to IT department, revert all transactions etc.
      Console.WriteLine("I can't seem to do that");
    }
  }

  class Program {
    public static void Main() {
      ProxyComputer pc = new ProxyComputer();
      MoreStrictComputer msc = new MoreStrictComputer();
      // fires onError instead of exception
      Console.WriteLine(pc.divide(1, 0)); // no problem
      Console.WriteLine(msc.divide(1, 0)); // this time it won't be so easy
    }
  } // only two levels of brackets

Spesso vedo blocchi di cattura vuoti (o banali). Ciò significa che i programmatori spesso considerano le eccezioni come nessuna situazione problematica, ma con un'attitudine standard alle eccezioni, devono comunque preoccuparsi di provare a catturare. Il secondo atteggiamento lo rende opzionale.

Quindi la domanda è: quale soluzione preferiresti? È una specie di schema o mi manchi qualcosa?

Modifica come suggerisce Paul Equis, throw e; deve essere predefinito sul comportamento dell'Error, non sul silenzio del do-nothing swalowing.

    
posta Jan Turoň 26.07.2011 - 22:16
fonte

2 risposte

2

Sono d'accordo con la risposta di Paul Equis che le eccezioni dovrebbero essere preferite, ma con un avvertimento. Una delle principali funzionalità delle eccezioni è che interrompono il flusso di controllo. Questo è solitamente desiderabile, ma se non lo è, allora qualche altro modello potrebbe essere utile per aumentare il sistema di eccezioni.

Ad esempio, supponi di scrivere un compilatore. Le eccezioni potrebbero non essere la scelta migliore qui, perché il lancio di un'eccezione arresta il processo di compilazione. Ciò significa che verrà segnalato solo il primo errore. Se vuoi continuare a leggere il codice sorgente per cercare di trovare più errori (come fanno i compilatori C # e VB), è necessario un altro sistema per segnalare errori al mondo esterno.

Il modo più semplice per gestirlo sarebbe salvare le eccezioni in una raccolta e quindi restituirla. Tuttavia, l'utilizzo di un delegato OnError potrebbe essere utile se si desidera offrire al chiamante l'opportunità di fornire consigli su come procedere dopo ciascun errore. Per me sembra uno scenario insolito. Se non chiedi al chiamante di gestire in modo veramente efficace la gestione degli errori, utilizzare alcuni flag per specificare il comportamento di gestione degli errori sarebbe meno complesso con cui lavorare.

    
risposta data 27.07.2011 - 00:29
fonte
10

Vorrei decisamente preferire le eccezioni a una funzione onError improvvisata.

Le eccezioni sono già integrate nella lingua. È il modo definito da C # per gestire stati imprevisti. I programmatori nuovi del tuo codice base comprenderanno già l'idea di un try-catch. Alcuni pattern di gestione onError improvvisati non apportano alcun vantaggio che io possa vedere e rendono il tuo codice meno standard.

Inoltre, le eccezioni forniscono un comportamento "fail-fast". Se non si rileva un'eccezione, si verificherà (in genere) un arresto anomalo. Ciò accade automaticamente e devi ingoiare esplicitamente un'eccezione affinché questo NON avvenga. Ma con il tuo approccio onError , è il comportamento opposto: tutti i fallimenti sono silenziosi a meno che tu non li ascolti.

Questa non è una buona cosa. Aumenta la distanza tra il punto in cui un errore è causato e dove è notato (ho sentito questo concetto chiamato "distanza di errore"). Il debug può essere abbastanza difficile e questo rende più difficile.

    
risposta data 26.07.2011 - 22:42
fonte

Leggi altre domande sui tag