Come nascondi la gestione degli errori? [duplicare]

3

Molte persone considerano le eccezioni un problema perché creano percorsi invisibili attraverso il codice. Ad esempio in questo frammento:

function writeToFile(text, filename):
  filehandle = open(filename)
  foreach line in text:
    filehandle.write(line)
  filehandle.close()

Il metodo write potrebbe generare un'eccezione. In tal caso, la funzione termina prematuramente e il filehandle non viene chiuso. Scrivere un codice di sicurezza è difficile.

D'altra parte l'aggiunta di una gestione esplicita degli errori come in C Cluster aumenta considerevolmente il codice. (Questa sembra essere l'opzione "altro" se non ti piacciono le eccezioni.)

function writeToFile(text, filename):
  filehandle = open(filename)
  if nullptr == filehandle:
    return
  foreach line in text:
    if(!filehandle.write(line)):
      filehandle.close()
      return
  filehandle.close()

Questo è solo un semplice esempio. Ho appena scritto il codice per fare un po 'di registrazione e tra il concetto astratto di "Just dump data in a file!" e la mia implementazione è una differenza di circa 200 linee.

Quindi cosa fanno le persone per mantenere leggibile il loro codice nonostante tutti quei casi angolari che devono essere controllati e gestiti?

Dobbiamo ricorrere alla programmazione alfabetica? :)

    
posta Sarien 05.06.2013 - 14:06
fonte

4 risposte

4

In una lingua che supporta la distruzione deterministica:

function writeToFile(text, filename):
  filehandle = open(filename)
  foreach line in text:
    filehandle.write(line)

e il tipo di filehandle si chiude solo nel distruttore. Ecco come funziona in C ++.

Le lingue senza distruzione deterministica spesso hanno costrutti sintattici per le risorse con scope: using-block in C #, try-with-resources in Java, with-block in Python:

function writeToFile(text, filename):
  with open(filename) as filehandle:
    foreach line in text:
      filehandle.write(line)

Se non lo fanno (Java < 7), ci sarà qualcosa come un tentativo ... infine costruire invece. E se anche questo non esiste, non usare quella lingua sbagliata.

Le lingue con un focus sulle chiusure (Ruby, la maggior parte dei linguaggi funzionali, come Haskell) usano spesso il pattern di intorno - scusa per aver abbandonato la sintassi simile a Python qui:

function writeToFile(text, filename) {
  withOpenFile(filename, lambda(filehandle) {
    foreach line in text {
      filehandle.write(line)
    }
  })
}

La linea di fondo è che vuoi un supporto dalla lingua per assicurarti che un codice venga eseguito indipendentemente da come lasci un blocco.

Ricorda che ciò di cui stiamo parlando non è in realtà una gestione degli errori. Riguarda la sicurezza delle eccezioni: codice che fa la cosa giusta quando viene lanciata un'eccezione. Devi ancora gestire l'eccezione da qualche parte, ma non si tratta di questo.

    
risposta data 05.06.2013 - 14:26
fonte
1

Non vedo nulla di sbagliato con il codice come il secondo esempio. Probabilmente i dettagli potrebbero essere migliorati un po ', ma, sì, un sacco di codice finisce per essere la gestione degli errori, la verifica dei dati e simili. Questo è essenziale per scrivere un buon codice.

Sarebbe un errore vederlo come "roba extra" che interferisce con il vero lavoro. È probabile che una tale mentalità conduca a un controllo degli errori inadeguato.

Una volta ho lavorato su alcuni software di aviazione in cui probabilmente l'algoritmo principale poteva essere scritto in circa 50 righe. Questo era circondato da migliaia di righe che esistevano principalmente per il controllo e la correzione degli errori. Questo può essere fastidioso, ma è piuttosto essenziale per una funzione come tenere un aereo in aria.

    
risposta data 05.06.2013 - 14:17
fonte
0
function writeToFile(text, filename):
  filehandle = open(filename)
  if (filehandle)
  {
    foreach line in text:
      if(!filehandle.write(line)):
        break;

    filehandle.close()
  }

non è così male e fa tutto ciò che volevi, con la gestione degli errori.

o

function writeToFile(text, filename):
  filehandle = open(filename)
  try
  {
    foreach line in text:
      filehandle.write(line)
  }
  catch (...)
  {
  }
  filehandle.close()

di nuovo, non troppo male e sarebbe meglio in una funzione più grande.

Penso che il problema qui non sia tanto la gestione degli errori, che è una parte importante e necessaria della codifica, tanto una parte della codifica e del codice "funzionale", ma la mentalità che dice che deve essere nascosta o che il codice di gestione degli errori è in qualche modo un segreto sporco che deve essere tenuto nascosto. Devi inserire molte altre cose (ad esempio la registrazione) che faranno gonfiare il tuo codice.

Forse l'approccio migliore per te è usare C / C ++ e usare macro per mantenere il tuo codice "estraneo" alle istruzioni a riga singola. O solo per imparare ad amare il codice di gestione degli errori.

    
risposta data 05.06.2013 - 14:15
fonte
0

Non ci sono dubbi, la gestione pulita degli errori è difficile senza ingombrare il codice. Anche se non è l'ideale, provo a mettere un blocco catch try nella funzione base che sto chiamando. Ad esempio, se ho un'applicazione di console di base configurata come segue (non intesa per essere specifica della lingua, solo un esempio):

Main () {  myFunction (); };

myFunction () {  // fare cose };

inserendo un blocco catch all'interno di main verranno rilevati eventuali errori che si verificano in myFunction. Purtroppo non è sempre garantito che funzioni per ogni tipo di programma e presenterà delle difficoltà quando si tratta di codice che non viene richiamato dalla stessa posizione (nell'esempio sopra riportato Main). Per le lingue come javascript, in cui è possibile passare una funzione come parametro, è possibile scrivere una semplice funzione wrapper che richiama semplicemente una funzione che è stata passata all'interno di un blocco catch try e restituisce il risultato.

Dove e come catturare con grazia gli errori dipende in parte anche dalla configurazione della logica del programma. Se la maggior parte del codice che potrebbe produrre errori si trova in una classe separata, potrebbe essere meglio gestire l'errore nella classe stessa e non preoccuparti di lanciarlo al chiamante.

In generale, provo a inserire un blocco catch try nelle mie funzioni di base (come Main nell'esempio) e un ulteriore try catch block ovunque sia necessario eseguire le azioni in caso di errore, come la chiusura di un lettore di file. Per la maggior parte, ciò accadrà la maggior parte degli errori che si verificano (ancora una volta dipende da come viene eseguito il codice) e fornirà ulteriore sicurezza quando necessario.

    
risposta data 05.06.2013 - 14:34
fonte

Leggi altre domande sui tag