Creazione di funzioni nidificate per ragioni puramente estetiche?

16

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

Dire che ho una funzione che elabora un blocco di dati: Function ProcessBigData . Diciamo che ho bisogno di diversi passaggi di processo, validi solo per quei dati: Step1 , Step2 , Step3 .

L'aproach 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 sbagliato a causa della mancanza di uno stile di codifica simile a quello degli altri colleghi:

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

    Function Step2:
        Step2..
        Step2..

    Step1() -> Step2()

Mi preoccupo principalmente se ci sono degli svantaggi per tale stile in Javascript e Python

Ci sono delle alternative che non vedo?

    
posta Slytael 11.04.2014 - 19:49
fonte

2 risposte

4

Non è così strano come potresti pensare. Ad esempio, in Standard ML è consuetudine limitare l'ambito delle funzioni di supporto. Certo, 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) le 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 sovraccarico nella creazione delle funzioni interne ogni volta che viene chiamata la funzione esterna (non so se JS o Python lo ottimizzano) ma sai cosa dicono sull'ottimizzazione prematura.

    
risposta data 11.04.2014 - 20:19
fonte
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 input (i) definiti ed output (s) (si pensi a la" S " in SOLID ). Un sottoattività non deve essere riutilizzabile: alcune persone tendono a pensare "Non dovrò mai chiamarlo da qualsiasi altra cosa, quindi perché scriverlo come una funzione?" ma questo è un errore.

Proverò a delineare i vantaggi e anche come si applica alle funzioni nidificate (chiusure) rispetto a un'altra funzione della classe. In generale, consiglierei di non usare chiusure a meno che non ne abbiate bisogno in particolare (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 della linea 2-20 sono facili da leggere. Il codice è per gli umani.

Nidificati o meno, ottieni principalmente il vantaggio della leggibilità, a meno che tu non stia utilizzando molte variabili dall'ambito principale, nel qual caso può essere altrettanto difficile da leggere.

Limita ambito variabile

Avere un'altra funzione ti costringe a limitare l'ambito delle variabili e in particolare a passare ciò di cui hai bisogno.

Questo spesso ti rende anche migliore il codice della struttura, perché se hai bisogno di una sorta di variabile di stato da un precedente "passo", potresti effettivamente scoprire che esiste in realtà un'altra sottoattività che dovrebbe essere scritta ed eseguita per ottenere quel valore. O in altre parole, rende più difficile scrivere blocchi di codice altamente accoppiati.

L'uso 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 è stata scritta. Questo è ancora più vero se stai modificando le variabili nell'ambito genitore (una pessima idea, in generale).

Test unitari

Ogni attività secondaria, implementata una funzione (o anche una classe) è una parte di codice indipendente e verificabile. I vantaggi del test delle unità e TDD sono ben documentati altrove.

L'utilizzo di funzioni / chiusure annidate 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 vi sia una necessità specifica di chiusura.

Lavorare su un team / Progetto top-down

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

Anche da solo, può essere utile quando si scrive codice per richiamare semplicemente un'attività secondaria che non esiste ancora, mentre si costruisce la funzionalità principale, e preoccuparsi di implementare l'attività secondaria solo dopo aver saputo che otterrà i risultati bisogno in modo significativo Questo è anche chiamato design / programmazione top-down.

Riutilizzo del codice

Va bene, quindi, nonostante quello che ho detto prima, a volte in realtà finisce per essere una ragione per riutilizzare una sottoattività per qualcos'altro. Non sto affatto sostenendo l'architettura-astronauta -ismo, ma solo scrivendo codice liberamente accoppiato, si potrebbe finire per trarre beneficio in seguito dal riutilizzo.

Spesso riutilizzare significa refactoring, il che è perfettamente previsto, ma il refactoring dei parametri di input su una piccola funzione standalone è MOLTO più facile che estrarlo da una funzione di linea 200+ mesi dopo che è stato scritto, che è davvero il mio punto qui.

Se si utilizza una funzione nidificata, riutilizzarla è in genere una questione di refactoring ad una funzione separata comunque, il che, di nuovo, è il motivo per cui direi che nested non è la strada da percorrere.

    
risposta data 11.04.2014 - 21:12
fonte

Leggi altre domande sui tag