Passare funzioni ad altre funzioni come parametri, cattiva pratica?


40

Stiamo cambiando 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 ed è stato consegnato a me. Ci lavoro da circa una settimana o poco fa e capisco il sistema, ma c'è una cosa che mi preoccupa. Sembra che ci siano molti passaggi di funzioni in funzioni. Ad esempio, la nostra classe che effettua la chiamata ai nostri server ha una funzione che chiamerà e passerà a un oggetto quando il processo è completo e gli errori sono stati gestiti ecc.

Mi sta dando quella "brutta sensazione" in cui mi sento come una pratica orribile e posso pensare ad alcuni motivi per cui, ma voglio una conferma prima di proporre una rielaborazione al sistema. Mi chiedevo se qualcuno avesse qualche esperienza con questo possibile problema?


22
Perché ti senti così? Hai qualche esperienza con la programmazione funzionale? (Presumo di no, ma dovresti dare un'occhiata)
Phoshi

8
Quindi considera questa una lezione! Ciò che l'insegnamento accademico e ciò che è effettivamente utile spesso hanno sorprendentemente poca sovrapposizione. C'è un intero mondo di tecniche di programmazione là fuori che non sono conformi a quel tipo di dogma.
Foshi,

32
Passare funzioni ad altre funzioni è un concetto così fondamentale che quando una lingua non lo supporta, le persone si fanno in quattro per creare banali "oggetti" il cui unico scopo è contenere la funzione in questione come un punto fermo.
Doval,

36
Ai miei tempi abbiamo chiamato queste funzioni pass-through "Callbacks"
Mike,

Risposte:


84

Non è un problema.

È una tecnica nota. Queste sono funzioni di ordine superiore (funzioni che accettano funzioni come parametri).

Questo tipo di funzione è anche un elemento base nella programmazione funzionale ed è ampiamente utilizzato in linguaggi funzionali come Haskell .

Tali funzioni non sono né buone né buone - se non hai mai incontrato la nozione e la tecnica all'inizio possono essere difficili da comprendere all'inizio, ma possono essere molto potenti e sono un buon strumento da avere nella cintura degli attrezzi.


Grazie per questo, non ho esperienza reale con la programmazione funzionale, quindi esaminerò. Semplicemente non stava bene perché, per mia poca conoscenza, quando dichiari qualcosa come una funzione privata, solo quella classe dovrebbe avervi accesso e quelle pubbliche dovrebbero essere chiamate con riferimento all'oggetto. Lo esaminerò correttamente e mi aspetto che lo apprezzerò ancora un po '!
Elliot Blackburn,


8
@BlueHat dichiarate qualcosa di privato se volete avere il controllo su come viene usato, se quell'uso è come un callback per funzioni specifiche, allora va bene
maniaco del cricchetto

5
Esattamente. Contrassegnarlo come privato significa che non vuoi che arrivi qualcun altro e accedervi per nome ogni volta che lo desiderano. Passare una funzione privata a qualcun altro perché si desidera specificamente che la chiamino nel modo in cui documentano per quel parametro, va assolutamente bene. Non deve essere diverso dal passare il valore di un campo privato come parametro ad un'altra funzione, che presumibilmente non si adatta male a te :-)
Steve Jessop

2
Vale la pena notare che se ti ritrovi a scrivere classi concrete con funzioni come parametri del costruttore, probabilmente stai sbagliando .
Gusdor,

30

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

Un callback è un pezzo di codice eseguibile che viene passato come argomento ad un altro codice, che dovrebbe richiamare (eseguire) l'argomento in un momento opportuno. L'invocazione può essere immediata come in un callback sincrono o potrebbe accadere in un secondo momento, come in un callback asincrono.

Pensa al codice asincrono per un secondo. Si passa in 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 quindi per inviare nuovamente i dati all'utente. È un cambiamento di mentalità.

Ho scritto una libreria che recupera i dati Torrent dal tuo seedbox. Stai utilizzando un ciclo di eventi non bloccante 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 loop di eventi e una delle richieste per ottenere le bancarelle di dati torrent di qualcuno. Ciò bloccherà l'intero ciclo. Quindi è necessario pensare in modo asincrono e utilizzare i callback: il ciclo continua a funzionare e il "restituire i dati all'utente" viene eseguito solo quando la funzione ha terminato l'esecuzione, quindi non è necessario attendere. Spara e dimentica.


1
+1 Per l'utilizzo della terminologia di callback. Incontro questo termine molto più spesso di "funzioni di ordine superiore".
Rodney Schuler,

Mi piace questa risposta, perché l'OP sembrava descrivere i callback, in particolare, e non solo le funzioni di ordine superiore in generale: "Ad esempio la nostra classe che effettua la chiamata ai nostri server assume una funzione che poi chiamerà e passerà un oggetto a quando il processo è completo e sono stati gestiti errori ecc. "
Ajedi32,

11

Questa non è una brutta cosa. In effetti, è un'ottima cosa.

Passare le funzioni alle funzioni è così importante per la programmazione che abbiamo inventato le funzioni lambda come scorciatoia. Ad esempio, si può usare lambda con algoritmi C ++ per scrivere codice molto compatto ma espressivo che consente a un algoritmo generico la possibilità di usare 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 una, ma non sempre). È quindi possibile creare una classe semplice che implementa tale interfaccia e passare un oggetto di quella classe a una funzione. Questa è una pietra miliare della programmazione guidata dagli eventi , in cui il codice a livello di framework (forse anche in un altro thread) deve chiamare un oggetto per cambiare stato in risposta a un'azione dell'utente. L' interfaccia di ActionListener di Java ne è un buon esempio.

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

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

Altri hanno menzionato la programmazione funzionale e come il passaggio di funzioni sia molto naturale in quei linguaggi. Lambdas e callback sono il modo in cui i linguaggi procedurali e OOP lo imitano e sono molto potenti e utili.


Inoltre, in C # delegati e lambda sono entrambi ampiamente utilizzati.
Evil Dog Pie,

6

Come già detto, non è una cattiva pratica. È semplicemente un modo per disaccoppiare e separare le responsabilità. Ad esempio, in OOP faresti qualcosa del genere:

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.


1
Questo idioma sta semplicemente racchiudendo la funzione in un'interfaccia, realizzando qualcosa di molto simile al passaggio di una funzione raw in.

Sì, volevo solo dimostrare che questa non è una pratica insolita.
Philipp Murry,

3

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

Ci sono alcuni potenziali svantaggi di semplici callback:

  • Effettuare una serie di chiamate può richiedere l'annidamento profondo dei callback.
  • La gestione degli errori può richiedere ripetizioni per ogni chiamata in una sequenza di chiamate.
  • Coordinare più chiamate è scomodo, come fare più chiamate contemporaneamente e poi fare qualcosa una volta che hanno finito.
  • Non esiste un modo generale per annullare una serie di chiamate.

Con semplici servizi Web il modo in cui lo fai funziona bene, ma diventa imbarazzante se hai bisogno di sequenze di chiamate più complesse. 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 javascript ).

Esse implicano comunque il passaggio di funzioni ad altre funzioni, ma le chiamate asincrone restituiscono un valore che accetta un callback anziché prendere direttamente un callback. Ciò offre maggiore flessibilità per comporre queste chiamate insieme. Qualcosa del genere può essere implementato abbastanza facilmente in ActionScript.


Vorrei anche aggiungere che l'uso non necessario dei callback può rendere più difficile seguire il flusso di esecuzione per chiunque legga il codice. Una chiamata esplicita è più semplice da comprendere rispetto a una richiamata impostata in una parte distante del codice. (Punti di interruzione per il salvataggio!) Tuttavia, questo è il modo in cui gli eventi si attivano nel browser e in altri sistemi basati su eventi; allora dobbiamo capire la strategia dell'emettitore di eventi per sapere quando e in quale ordine verranno chiamati i gestori di eventi.
joeytwiddle,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.