Passare funzioni in altre funzioni come parametri, cattive pratiche?

39

Siamo stati in procinto di cambiare il modo in cui la nostra applicazione AS3 parla al nostro back-end e stiamo implementando un sistema REST per sostituire quello vecchio.

Purtroppo lo sviluppatore che ha iniziato il lavoro è ora in congedo per malattia a lungo termine e mi è stato consegnato. Ci lavoro da circa una settimana e ora capisco il sistema, ma c'è una cosa che mi preoccupa. Sembra che ci sia un sacco di passaggio di funzioni in funzioni. Ad esempio la nostra classe che effettua la chiamata ai nostri server accetta una funzione che chiamerà e passerà un oggetto a quando il processo è completo e gli errori sono stati gestiti, ecc.

Mi sta dando quella "brutta sensazione" in cui sento che è una pratica orribile e posso pensare ad alcuni motivi per cui voglio ottenere una conferma prima di proporre una rielaborazione al sistema. Mi stavo chiedendo se qualcuno avesse qualche esperienza con questo possibile problema?

    
posta Elliot Blackburn 20.08.2014 - 11:39
fonte

5 risposte

83

Non è un problema.

È una tecnica conosciuta. Queste sono funzioni di ordine superiore (funzioni che assumono funzioni come parametri).

Questo tipo di funzione è anche un elemento di base nella programmazione funzionale ed è ampiamente utilizzato in linguaggi funzionali come < a href="http://www.haskell.org/haskellwiki/Haskell"> Haskell .

Tali funzioni non sono male o buone - se non hai mai incontrato la nozione e la tecnica che possono essere difficili da cogliere all'inizio, ma possono essere molto potenti e sono un buon strumento da avere nel tuo cinturone.

    
risposta data 20.08.2014 - 11:48
fonte
30

Non sono solo usati per la programmazione funzionale. Possono anche essere chiamati callback :

A callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. The invocation may be immediate as in a synchronous callback or it might happen at later time, as in an asynchronous callback.

Pensa al codice asincrono per un secondo. Si passa a una funzione che, ad esempio, invia i dati all'utente. Solo quando il codice è stato completato, si richiama questa funzione con il risultato della risposta, che la funzione utilizza per inviare nuovamente i dati all'utente. È un cambiamento di mentalità.

Ho scritto una libreria che recupera i dati Torrent dalla tua seedbox. Si sta utilizzando un ciclo di eventi non bloccanti per eseguire questa libreria e ottenere dati, quindi restituirli all'utente (ad esempio, in un contesto Websocket). Immagina di avere 5 persone connesse in questo ciclo di eventi e una delle richieste per ottenere blocchi di dati torrent di qualcuno. Ciò bloccherà l'intero ciclo. Quindi è necessario pensare in modo asincrono e utilizzare i callback: il ciclo continua a essere eseguito e "il rinvio dei dati all'utente" viene eseguito solo quando la funzione ha terminato l'esecuzione, quindi non è necessario attendere. Fuoco e dimentica.

    
risposta data 20.08.2014 - 17:45
fonte
11

Questa non è una brutta cosa. In effetti, è una cosa molto buona.

Il passaggio di funzioni in funzioni è così importante per la programmazione che abbiamo inventato funzioni lambda come una scorciatoia. Ad esempio, uno può utilizzare lambdas con gli algoritmi C ++ per scrivere molto compatto codice ancora espressivo che consente ad un algoritmo generico di utilizzare variabili locali e altri stati per fare cose come la ricerca e l'ordinamento.

Le librerie orientate agli oggetti possono anche avere callback che sono essenzialmente interfacce che specificano un piccolo numero di funzioni ( idealmente uno, ma non sempre). Si può quindi creare una semplice classe che implementa quell'interfaccia e passare un oggetto di quella classe attraverso una funzione. Questa è una pietra miliare della programmazione basata sugli eventi , dove il codice a livello di framework (forse anche in un altro thread) deve chiamare in un oggetto per cambiare stato in risposta a un'azione dell'utente. L'interfaccia ActionListener di Java è un buon esempio di questo.

Tecnicamente, un functor C ++ è anche un tipo di oggetto callback che sfrutta lo zucchero sintattico, operator()() , per fare la stessa cosa.

Infine, ci sono dei puntatori di funzione in stile C che dovrebbero essere usati solo in C. Non entrerò nei dettagli, li citerò solo per completezza. Le altre astrazioni menzionate sopra sono di gran lunga superiori e dovrebbero essere usate nelle lingue che le hanno.

Altri hanno menzionato la programmazione funzionale e il modo in cui le funzioni di passaggio sono molto naturali in quelle lingue. Lambda e callback sono il modo in cui i linguaggi procedurali e OOP lo imitano, e sono molto potenti e utili.

    
risposta data 21.08.2014 - 06:51
fonte
6

Come già detto, non è una cattiva pratica. È semplicemente un modo di disaccoppiare e separare la responsabilità. Ad esempio, in OOP si dovrebbe fare qualcosa di simile:

public void doSomethingGeneric(ISpecifier specifier) {
    //do generic stuff
    specifier.doSomethingSpecific();
    //do some other generic stuff
}

Il metodo generico sta delegando un'attività specifica, di cui non sa nulla, a un altro oggetto che implementa un'interfaccia. Il metodo generico conosce solo questa interfaccia. Nel tuo caso, questa interfaccia sarebbe una funzione da chiamare.

    
risposta data 20.08.2014 - 11:55
fonte
3

In generale, non c'è niente di sbagliato nel passare le funzioni ad altre funzioni. Se stai facendo chiamate asincrone e vuoi fare qualcosa con il risultato, allora avresti bisogno di una sorta di meccanismo di callback.

Tuttavia esistono alcuni potenziali svantaggi delle semplici callback:

  • Effettuare una serie di chiamate può richiedere una nidificazione profonda dei callback.
  • La gestione degli errori può richiedere la ripetizione per ciascuna chiamata in una sequenza di chiamate.
  • Il coordinamento di più chiamate è scomodo, come fare più chiamate contemporaneamente e poi facendo qualcosa una volta sono tutti finiti.
  • Non esiste un modo generale per annullare un gruppo di chiamate.

Con semplici webservices il modo in cui lo fai funziona bene, ma diventa complicato se hai bisogno di sequenze più complesse di chiamate. Ci sono alcune alternative però. Con JavaScript, ad esempio, c'è stato uno spostamento verso l'uso delle promesse ( Cosa c'è di così bello nelle promesse di javascript ).

Ancora implicano il passaggio di funzioni ad altre funzioni, ma le chiamate asincrone restituiscono un valore che accetta una richiamata invece di richiamare direttamente una callback. Ciò offre maggiore flessibilità per la composizione di queste chiamate. Qualcosa di simile può essere implementato abbastanza facilmente in ActionScript.

    
risposta data 20.08.2014 - 22:04
fonte

Leggi altre domande sui tag