Creazione di funzioni nidificate per motivi puramente estetici?


16

Mi sono sempre chiesto cosa pensano gli altri programmatori dell'idea di creare funzioni estetiche pure.

Dire che ho una funzione che elabora un blocco di dati: Function ProcessBigData. Dire che ho bisogno diverse fasi del processo, valida solo per i dati: Step1, Step2,Step3 .

L'approccio normale che vedo di più nel codice sorgente è scrivere commenti in questo modo:

Function ProcessBigData:
    # Does Step1
    Step1..
    Step1..

    #Does Step2
    Step2..
    Step2..

Quello che faccio di solito, ma mi sono sempre sentito male a causa della mancanza di tale stile di programmazione da parte di colleghi colleghi è:

Function ProcessBigData:
    Function Step1:
        Step1..
        Step1..

    Function Step2:
        Step2..
        Step2..

    Step1() -> Step2()

Sono principalmente preoccupato se ci sono degli svantaggi per tale stile Javascript e Python

Ci sono alternative che non vedo?


3
Non posso dire nulla su Python, ma per Javascript, c'è un costo prestazionale per le funzioni nidificate: la maggior parte dei motori JavaScript utilizza una struttura simile a un elenco collegato per rappresentare l'ambito variabile. L'aggiunta di un ulteriore livello di funzioni obbliga quindi il motore a cercare una struttura dati più lunga / più grande durante la risoluzione delle variabili. D'altra parte, la radice di tutto il male è, ovviamente, l'ottimizzazione prematura. :)
Marco,

Risposte:


4

Non è strano come potresti pensare. Ad esempio, in Standard ML è consuetudine limitare l'ambito delle funzioni di supporto. Concesso, SML ha la sintassi per facilitarla:

local
    fun recursion_helper (iteration_variable, accumulator) =
        ... (* implementation goes here *)
in
    fun recursive_function (arg) = recursion_helper(arg, 0);
end

Considererei questo buon stile, dato che 1) piccole funzioni facilitano il ragionamento sul programma e 2) segnala al lettore che queste funzioni non sono utilizzate al di fuori di tale ambito.

Suppongo sia possibile che ci sia un certo sovraccarico nel creare le funzioni interne ogni volta che viene chiamata la funzione esterna (non so se JS o Python lo ottimizzano via) ma sai cosa dicono sull'ottimizzazione prematura.


11

Di solito è una buona cosa farlo quando possibile, ma mi piace pensare a questo tipo di lavoro non come "passi", ma come sottoattività .

Una sottoattività è un'unità di lavoro specifica che può essere eseguita: ha una responsabilità specifica e ha definito input (s) e output (s) (pensa alla "S" in SOLID ). Una sottoattività non deve essere riutilizzabile: alcune persone tendono a pensare "Non dovrò mai chiamare questo da qualsiasi altra cosa, quindi perché scriverlo come funzione?" ma questo è un errore.

Proverò anche a delineare i vantaggi e anche il modo in cui si applica alle funzioni nidificate (chiusure) rispetto a un'altra funzione della classe. In generale, consiglierei di non usare le chiusure a meno che tu non ne abbia specificamente bisogno (ci sono molti usi, ma separare il codice in blocchi logici non è uno di questi).

Leggibilità.

Oltre 200 righe di codice procedurale (corpo di una funzione) sono difficili da leggere. Le funzioni da 2 a 20 righe sono facili da leggere. Il codice è per l'uomo.

Annidato o no, si ottiene principalmente il vantaggio della leggibilità, a meno che non si utilizzino molte variabili dall'ambito padre, nel qual caso può essere altrettanto difficile da leggere.

Limitare l'ambito variabile

Avere un'altra funzione ti costringe a limitare l'ambito variabile e passare specificamente ciò di cui hai bisogno.

Questo spesso rende anche il codice della struttura migliore, perché se hai bisogno di una sorta di variabile di stato da un precedente "passo", potresti effettivamente trovare un altro sotto-compito che dovrebbe essere scritto ed eseguito per primo per ottenere quel valore. O in altre parole, rende più difficile scrivere blocchi di codice altamente accoppiati.

La presenza di funzioni nidificate consente di accedere alle variabili nell'ambito genitore dall'interno della funzione nidificata (chiusura). Questo può essere molto utile, ma può anche portare a bug sottili e difficili da trovare poiché l'esecuzione della funzione potrebbe non avvenire nel modo in cui è scritta. Questo è ancora più vero se stai modificando le variabili nell'ambito genitore (una pessima idea, in generale).

Test unitari

Ogni sottoattività, implementata una funzione (o persino una classe) è un pezzo di codice autonomo e testabile. I vantaggi del test unitario e del TDD sono ben documentati altrove.

L'uso di funzioni / chiusure nidificate non consente il test dell'unità. Per me, questo è un rompicapo e il motivo per cui dovresti solo un'altra funzione, a meno che non ci sia una necessità specifica di una chiusura.

Lavorare su un team / Progettazione dall'alto verso il basso

Le attività secondarie possono essere scritte da persone diverse, indipendentemente, se necessario.

Anche da solo, può essere utile durante la scrittura del codice semplicemente invocare alcune attività secondarie che non esistono ancora, mentre si sviluppa la funzionalità principale e preoccuparsi di implementare effettivamente la attività secondaria solo dopo aver saputo che otterrà i risultati necessari in un modo significativo. Questo è anche chiamato progettazione / programmazione top-down.

Riutilizzo del codice

Va bene, quindi, nonostante quello che ho detto prima, a volte in realtà finisce per essere un motivo per riutilizzare una sottoattività per qualcos'altro. Non sto affatto sostenendo il "ismo dell'astronauta dell'architettura "ma solo che scrivendo un codice liberamente accoppiato, potresti finire per beneficiare in seguito del riutilizzo.

Spesso il riutilizzo significa un po 'di refactoring, che è perfettamente previsto, ma il refactoring dei parametri di input in una piccola funzione autonoma è MOLTO più semplice che estrarlo da una funzione di oltre 200 mesi dopo la sua scrittura, che è davvero il mio punto qui.

Se si utilizza una funzione nidificata, il riutilizzo in genere è comunque una questione di refactoring in una funzione separata, il che è ancora una volta il motivo per cui direi che il nidificato non è la strada da percorrere.


2
Questi sono alcuni punti davvero validi per l'utilizzo delle funzioni in generale, ma non ho ricevuto la tua risposta se pensi che le funzioni NESTED siano una buona idea. O ottieni le funzioni nell'ambito dell'upstream?
Slytael,

Scusatemi, sono rimasto coinvolto negli altri benefici che ho dimenticato di affrontare quella parte. :) Modificato.
Gregreg,
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.