Finalmente blocco per i metodi - è una cattiva idea?

1

Il blocco finally per una struttura try-catch è ben noto e offre un modo davvero semplice ed elegante per gestire un codice obbligatorio.

Pertanto, non vedo ragioni per cui non dovrebbe essere utile anche per i metodi. Per esempio, diciamo che sto scrivendo una logica molto complicata in un metodo, e mi aspetto di finire con un gruppo di bandiere booleane che a loro volta porteranno ad alcune decisioni. Molte volte in questo tipo di metodi ho rami di codice in cui vorrei "interrompere" il flusso e semplicemente eseguire le "cose reali" per cui ho chiamato questo metodo, con le informazioni soddisfacenti che ho raccolto finora.

Quindi, perché non c'è un tale schema? O c'è?

    
posta Elist 20.06.2013 - 23:56
fonte

7 risposte

8

La prima cosa è: non fare funzioni troppo complesse a cose complicate. Se non puoi evitare funzioni complesse ci sono diversi strumenti per gestire la situazione.

In C e in altre lingue, le persone utilizzano spesso goto per il motivo.

void func() {
    /* .. code .. */
    if (condition()) goto end;
    /* ... more code ...*/
end:
    cleanup();
}

Ora ad alcune persone non piace goto , anche in questi casi in modo che emulino goto utilizzando un ciclo do { . } while(0) :

void func() {
    do {
        /* .. code .. */
        if (condition()) break;
        /* ... more code ...*/
    } while(0);
    cleanup();
}

alcune lingue, per esempio PHP consente di dare un parametro al parametro break per saltare più livelli fuori:

function func() {
    do {
        /* .. code .. */
        if(something() {
          /* some code */
          if (condition()) break 2;
          /* ... */
        }
        /* ... more code ...*/
    } while(0);
    cleanup();
}

ma ci sono linguaggi con più costrutti di alto livello. C ++ per esempio ha dei distruttori, lì la gente usa il modello RAII

struct cleanup {
    ~cleanup() {
        /* .. do cleanup ... */
    }
 }

void func() {
    cleanup cleaner; // this stack object will be destroyed on exit, for that the destructor is being called
    if (condition()) return;
    /* ... more code ...*/
}

Alcuni altri linguaggi non hanno goto e non hanno un'interruzione annidata e nessun distruttore, quindi i programmatori Java devono convivere con nidificati se si utilizzano le eccezioni ... o rendono il codice più semplice.

public static void func() {
    try {
        /* .. code .. */
        if(something() {
          /* some code */
          if (condition()) throw new ControlFlowException();
          /* ... */
        }
        /* ... more code ...*/
    } catch (ControlFlowException e) {
        /* ignore */
    } finally {
        cleanup();
    }
}

Quindi sì, i concetti esistono e differiscono per lingua.

    
risposta data 21.06.2013 - 00:25
fonte
4

La mia ipotesi è che non ci sia un enorme bisogno di quel tipo di funzionalità per due motivi:

  1. La maggior parte delle lingue offre già un controllo del flusso piuttosto esplicito con costrutti di looping e ramificazioni.
  2. Uno degli usi più comuni di finally è garantire che le risorse vengano smaltite in modo sicuro. Se il tuo metodo è così complicato da averne bisogno, dovresti probabilmente ridefinirlo in parti più gestibili.
risposta data 21.06.2013 - 00:27
fonte
2

Vale la pena notare che C # ha il using istruzione che chiamerà automaticamente Dispose() sull'oggetto quando lascia l'ambito. Ciò include lo smaltimento dopo un'istruzione return , quindi in questo modo si comporta come finally .

Si noti che questo è legato all'oggetto , non legato alla funzione. Ma gestisce l'usecase principale per un finally in una funzione.

    
risposta data 21.06.2013 - 01:13
fonte
2

Il go linguaggio di programmazione ha qualcosa di simile a questa idea: il defer dichiarazione. Le funzioni posticipate vengono chiamate quando la funzione di chiusura si chiude per qualsiasi motivo e possono essere chiamate al metodo o funzioni semplici, ad esempio:

func fileProcessor(filename string) (err error) {
  var file *os.File
  if file, err = os.Open(filename); err != nil {
    return
  }
  defer func() {
    fmt.Printf("Closing file\n")
    file.Close()
  }()
  fmt.Printf("Processing file\n")
  return
}

In questo caso, la funzione posticipata è anonima. Potrebbe anche essere solo:

defer file.Close()

Ma la routine anonima ha permesso a Printfs nell'esempio di evidenziare l'ordine di esecuzione.

In pratica, trovo i defers come un modo pulito per garantire il rilascio di risorse acquisite localmente. L'approccio non si trova comunemente in altre lingue, ma trovo la sua presenza in Go come benvenuto e vorrei vederlo altrove.

    
risposta data 16.09.2013 - 22:17
fonte
1

Il metodo di estrazione sembra un buon piano: suddividi il metodo in una funzione di "inizializzazione", una funzione "fai le tue cose" e una "finalizzazione". In questo modo la finalizzazione sarà sempre eseguita indipendentemente da ciò che accade in "fai la tua cosa". Tranne che nel caso delle eccezioni, naturalmente, ma è qui che entra in gioco il blocco finally delle tue lingue. Quindi inserirai la funzione "finalizzazione" nel blocco finally.

procedure MainFunction;
begin
  InitializationFunction;
  try
    DoYourThingFunction;
  finally
    FinalizationFunction;
  end;
end;
    
risposta data 21.06.2013 - 10:55
fonte
1

finally causa un sacco di ripetizioni del codice. Se utilizzi una risorsa Foo in 100 luoghi e la pulizia di Foo richiede 3 righe, i blocchi finally aggiungerebbero 300 righe di codice, ma un distruttore aggiunge solo 3 righe in totale.

    
risposta data 21.06.2013 - 13:27
fonte
0

Questo rende davvero più leggibile il tuo codice rispetto alle alternative? Potresti già provare / finalmente aggirare il codice all'interno della tua funzione o potresti inserire il codice "Finally" subito dopo aver chiamato il metodo.

Sebbene sia un concetto ragionevole, ogni nuova funzionalità inserita in una lingua richiede molte ore di programmazione per imparare e comprendere: centinaia di migliaia ... Se esiste un modo per utilizzare un costrutto esistente senza causando molte duplicazioni, errori di stampa o confusione, sarebbe molto meglio usare il meccanismo esistente.

    
risposta data 21.06.2013 - 06:10
fonte

Leggi altre domande sui tag