Cosa è dimostrato come una buona lunghezza massima di una funzione? [chiuso]


44

La lunghezza della funzione influisce sulla produttività di un programmatore? In tal caso, qual è un buon numero massimo di righe per evitare la perdita di produttività?

Dal momento che si tratta di un argomento molto apprezzato, eseguire il backup del reclamo con alcuni dati.


6
La lunghezza non deve essere misurata in LOC, ma nella quantità di tempo necessaria per comprendere esattamente cosa fa. E quella lunghezza, non dovrebbe essere più di un minuto. Se non riesco a capirlo in pochi secondi, probabilmente sta facendo troppo ... dopo un minuto, lo è sicuramente.
CaffGeek,


13
La lunghezza massima dovrebbe essere 17.
ThomasX

1
Pensa a S in SOLID.
Kris Krause,

1
@CaffGeek O forse la funzione sta semplicemente facendo qualcosa di non banale. Ho visto funzioni che mi richiederebbero giorni per comprendere appieno. Anche le funzioni in cui comprendo tutti i concetti coinvolti possono facilmente richiedere mezz'ora per elaborare i dettagli. Mentre è bello avere funzioni banali, molti problemi sono semplicemente intrinsecamente difficili.
Codici A Caos il

Risposte:


46

Da quando ho iniziato questa folle racchetta nel 1970, ho visto esattamente un modulo che doveva essere realmente più di una pagina stampata (circa 60 righe). Ho visto molti moduli più lunghi.

Del resto, ho scritto moduli più lunghi, ma di solito erano grandi macchine a stati finiti scritte come grandi dichiarazioni switch.

Parte del problema sembra essere che ai programmatori in questi giorni non viene insegnato a modulare le cose.

Anche gli standard di codifica che massimizzano lo spreco di spazio verticale sembrano essere parte del problema. (Devo ancora incontrare un software manager che ha letto la " Psicologia della programmazione del computer " di Gerald Weinberg . Weinberg sottolinea che numerosi studi hanno dimostrato che la comprensione del programmatore è essenzialmente limitata a ciò che il programmatore può vedere in un dato istante. Se il il programmatore deve scorrere o girare una pagina, la loro comprensione diminuisce in modo significativo: devono ricordare e astrarre.)

Rimango convinto che molti dei ben documentati incrementi di produttività del programmatore da FORTH erano dovuti al sistema "a blocchi" FORTH per il codice sorgente: i moduli erano fortemente limitati a un massimo assoluto di 16 righe di 64 caratteri. Potresti considerare il fattore all'infinito, ma non puoi in nessun caso scrivere una routine di 17 righe.


3
L'intera filosofia di FORTH è stata progettata per incoraggiare questo ... Avevi intenzione di progettare il tuo vocabolario, spezzando incessantemente il tuo programma in pezzi sempre più piccoli, finendo con meno di una sceneggiatura e più di un dizionario. I limiti di lunghezza da soli non lo fanno: vedrai una routine divisa in pezzi arbitrari solo per soddisfare alcuni standard di codifica. Penso che tu abbia assolutamente ragione nel sospettare che ai programmatori non sia semplicemente "insegnato a modulare le cose"; questa avrebbe dovuto essere una delle grandi vittorie di OOP, ma per varie ragioni è spesso de-enfatizzata come obiettivo a se stessa.
Shog9

1
@Sig. CRT: i limiti di lunghezza nelle implementazioni FORTH orientate ai blocchi FORALED modularization. La natura interattiva della maggior parte dei FORTH aiuta, incoraggiando piccoli moduli e test rapidi di tali moduli. D85, un FORTH basato su file, non ha forzato la modularizzazione, e ho visto i ragazzi che giocano con D85 scrivere un sacco di stream-of-programmatore-coscienza run-on moduli per sempre. Da qui la mia convinzione. (Per quello che vale, Liz Rather non è d'accordo con me. Pensa che sia principalmente l'interattività a dare a FORTH l'incremento della produttività.)
John R. Strohm,

2
+1 per presentarmi il grande libro "Psicologia della programmazione informatica" :)
Samuel

30

Qual è la taglia giusta, davvero?

Dipende dalla lingua che usi, ma in generale (e per i miei gusti personali):

  • Idealmente , meno di 25 righe.
  • Accettabilmente , meno di 35 righe.

Se è di più, allora è qualcosa che devo tornare più tardi e rielaborare.

Ma realisticamente , qualsiasi dimensione deve essere quando devi consegnare qualcosa e che ha più senso al momento sputarli in quel modo, rende talvolta più facile per qualcuno rivederlo prima della spedizione. (ma ci torno ancora più tardi).

(Recentemente il mio team ha eseguito un programma sul nostro codebase: abbiamo trovato classe con 197 metodi e un altro con solo 3 metodi ma uno di questi era 600 linee. Gioco carino: qual è il peggio dei 2 mali?)


Ora per una risposta più zen ... In generale è considerata una buona pratica (TM) citare uno o due grandi uomini, quindi ecco qui:

Tutto dovrebbe essere reso il più semplice possibile, ma non più semplice. - A. Einstein

La perfezione si ottiene finalmente non quando non c'è più nulla da aggiungere, ma quando non c'è più nulla da togliere. - A. de Saint Exupéry


Addendum su stili di commento

Come aggiunta a questo, le tue funzioni dovrebbero avere nomi chiari che spiegano il loro intento. Per quanto riguarda i commenti, di solito non faccio commenti all'interno di una funzione:

  • i commenti dicono "perché?" ,
  • il codice dice "come?" .

È sufficiente un blocco di commenti nella parte superiore di ciascuna funzione (che richiede una spiegazione). Se la tua funzione è piccola e i nomi delle funzioni sono abbastanza espliciti, allora dovresti solo dire cosa vuoi ottenere e perché. Uso i commenti incorporati solo per i campi in alcune lingue o all'inizio del blocco per le funzioni che infrangono le regole della riga 25-35 se l'intento non è chiaro. Uso un commento a blocchi all'interno del codice quando si verifica una situazione eccezionale (un blocco catch in cui non è necessario o che si desidera fare qualcosa dovrebbe avere un commento che spiega perché, ad esempio).

Per ulteriori informazioni, leggi la mia risposta su Stile e consigli sul codice di commento


@haylem Immagino che questa sia la versione del programmatore di Freddy vs. Jason :-)
Gaurav,

sono d'accordo, tuttavia vorrei aggiungere che dovrebbe avere le dimensioni di una pagina dello schermo. se hai 72 linee in una pagina dello schermo, la funzione non deve superare 72 linee.
Visualizza nome

@ Si tratta di garantire che una singola funzione venga mantenuta in una pagina dello schermo, così da non scorrere, ma di solito non è la mia preoccupazione principale. La mia preoccupazione è il tempo necessario per elaborare le informazioni durante la lettura di una funzione, ed è quasi istantaneo se è chiaramente scritto in 25 righe. Diventa solo una questione di seguire le chiamate di funzione. 72 è troppo grande per me (in più, cosa succede se hai schermi divisi? E questo dipenderebbe dal carattere. Ma sono d'accordo per il valore storico della raccomandazione)
haylem

1
Certo, a volte hai delle funzioni in cui copi solo 70 campi diversi in 70 luoghi diversi. Lo uso principalmente ttper generarli, ma a volte sei bloccato con una funzione long-ass (o una funzione long ass) che in realtà non fa nulla di interessante, quindi non è un vero problema.
configuratore

1
Quando la funzione è ad es. Map(x => x.Property1); Map(x => x.Property2); Map(x => x.Property3);È abbastanza chiaro che è tutto abbastanza uguale. (Nota che questo è solo un esempio; questo tipo di funzione si apre di volta in volta)
configuratore

12

Secondo me, ogni funzione dovrebbe essere il più piccola possibile. Ogni funzione dovrebbe fare solo una cosa e farlo bene. Questo non risponde davvero alla domanda di massima lunghezza, ma sono più i miei sentimenti sulla lunghezza delle funzioni.

Per usare le parole di Zio Bob, "Estrai finché non riesci più ad estrarre. Estrai fino allo sfinimento".


2
Cosa intendi con il più piccolo possibile? Non sarebbe il più piccolo possibile avere ciascuna funzione con solo due linee: una per fare una singola operazione e un'altra che chiama una funzione per fare il resto?
Kelmikra,

Zio Bob di nuovo. Pensa per te. Non ascoltare gli zii. Ti portano fuori strada.
gnasher729,

10

Quale dovrebbe essere l'altezza massima di un edificio? Dipende da dove si trova la build o dall'altezza desiderata.
Puoi ottenere risposte diverse da persone diverse che provengono da città diverse.
Alcune funzioni di script e gestori di interrupt del kernel sono molto lunghi.


Sono totalmente d'accordo con te. Mi piacciono le metafore. :) Un edificio di tre piani potrebbe essere stato costruito da un pazzo architetto che non sa dove mettere un'uscita di sicurezza valida e un altro edificio potrebbe avere dieci piani ed essere un perfetto progetto architettonico. Dobbiamo sempre tenere presente che la leggibilità e la manutenibilità dovrebbero essere la ragione principale per rifattorizzare un metodo per ridurne le dimensioni e non le dimensioni stesse. Non è possibile costruire una città con il 90% di grattacieli se non nei film di fantascienza. :)
Samuel,

10

Un metodo che funziona per me è: posso avere una parte di una funzione più lunga dare un nome che abbia un senso. Penso che la lunghezza di un metodo non sia così importante come una buona denominazione. Il metodo dovrebbe fare ciò che dice il nome, né più né meno. E dovresti essere in grado di dare un buon nome. Se non riesci a nominare bene il tuo metodo, probabilmente il codice non è buono messo insieme.


E il tuo metodo dovrebbe fare solo una cosa per avere un buon nome ... no 'And', 'Or' o qualsiasi altra cosa che faccia il nome del metodo lungo 50 caratteri.
Samuel,

10

Finché deve essere per fare ciò che deve fare, ma non più.


come si dice in c, "In c non c'è problema che non si possa risolvere aggiungendo un altro puntatore a quel puntatore." potresti sempre aggiungere un'altra funzione al di sotto, la sua domanda sarebbe alla tua epifania "dove finisce?"
Visualizza nome

1
In realtà lo capovolgerei e direi "più corto che deve essere, ma non più corto", ma ottieni il mio +1 per essere abbastanza vicino :)
Ben Hughes,

6

Penso che ci sia un compromesso. Se hai molti metodi brevi, è spesso più difficile eseguirne il debug rispetto a un metodo lungo. Se devi saltare l'editor 20 o 30 volte diverse per tracciare una chiamata del metodo, sarà difficile tenerlo tutto nella tua testa. Nel frattempo, se esiste un metodo chiaro ben scritto, anche se è composto da 100 righe, è spesso più facile tenerlo in mente.

La vera domanda è perché gli elementi dovrebbero essere in diversi metodi e la risposta fornita sopra è il riutilizzo del codice. Se non stai riutilizzando il codice (o non lo sai), potrebbe essere logico lasciarlo in un unico metodo facile da seguire e quindi, quando dovrai riutilizzarlo, dividi le parti che devono riutilizzare usando in metodi più piccoli.

In realtà, parte del buon metodo di progettazione sta realizzando metodi funzionalmente coerenti (essenzialmente fanno una cosa). La lunghezza dei metodi non ha importanza. Se una funzione fa una cosa ben definita ed è di 1.000 righe, allora è un buon metodo. Se una funzione fa 3 o 4 cose ed è solo 15 linee, allora è un cattivo metodo ...


Mi piacciono i metodi brevi.
Marcie,

Mi piace quello che hai detto perché dire che un metodo non dovrebbe avere più di 10 righe è a mio avviso un'utopia. Ok, è una buona regola da tenere a mente ogni volta che scrivi un metodo ma non dovrebbe essere una regola matematica come 1 + 1 = 2. Se rispetti principi come KISS, DRY, YAGNI, ecc ... ei tuoi metodi sono commenti non completi che spiegano alcuni dettagli perché sono troppo lunghi, i metodi possono avere 100 righe di codice e potrebbero essere totalmente puliti da capire e mantenere. Tuttavia, dovrebbe essere più un'eccezione che un'abitudine. Penso che il caso di switch nel metodo di fabbrica sia un buon esempio di eccezione.
Samuel,

5

Trovo più facile tenere traccia di ciò che sto facendo se riesco a vedere l'intera funzione in una volta. Quindi ecco come preferisco scrivere funzioni:

  1. Abbastanza corto da adattarsi al mio monitor con un carattere ragionevole.
  2. Se deve essere più lungo del n. 1, abbastanza corto per stampare su un pezzo di carta con un carattere ragionevole.
  3. Se deve essere più lungo del n. 2, abbastanza corto per stampare 2 in su su un pezzo di carta.

Raramente scrivo funzioni più lunghe di così. La maggior parte di questi sono dichiarazioni switch C / C ++ giganti.


Abbastanza corto da adattarsi a un monitor è bello ma specifica il tipo e la dimensione del carattere. La carta non dovrebbe essere una regola secondo me perché siamo nel 2013 e chi stampa ancora il codice su carta, chi stampa solo per vedere se si adatta a un formato carta? Con strumenti come Visual Studio, intellisense, non c'è più motivo di analizzare il codice con la carta.
Samuel,

5

Per me, una funzione è la lunghezza che deve essere. Il più delle volte che lo divido è quando riutilizzerò il codice.

Fondamentalmente, mi atterrò alla principale "alta coesione, basso accoppiamento" e non ci sono vincoli sulla lunghezza.


5

La domanda dovrebbe essere: quante cose dovrebbe fare una funzione. E di solito, è raro che tu abbia bisogno di 100 linee per fare "una" cosa. Ancora una volta dipende dal livello dal quale stai guardando il codice: l'hashing di una password è una cosa? O l'hashing e il salvataggio della password sono una cosa?

Direi, inizia con il salvataggio della password come una funzione. Quando ritieni che l'hash sia diverso e rifletti il ​​codice. Non sono un programmatore esperto in alcun modo, ma IMHO, l'intera idea delle funzioni inizia in piccolo è che più atomiche sono le tue funzioni, maggiori sono le possibilità di riutilizzo del codice, non dovendo mai fare lo stesso cambiamento in più di un posto , eccetera.

Ho visto le procedure SQL memorizzate che funzionano su più di 1000 righe. Anche il numero di righe di stored procedure è inferiore a 50? Non lo so, ma rende la lettura del codice, inferno. Non solo devi continuare a scorrere su e giù, ma devi dare ad alcune righe di codice un nome come "this does validation1", "this update in the database", ecc. - un lavoro che il programmatore avrebbe dovuto fare.


+1 solo per il primo paragrafo. tutto il resto è relativo al caso su cui stai lavorando.
Visualizza nome

E come può essere utile suddividerlo in 50 funzioni? Quando le funzioni devono comunicare, quindi non finirai con 500 righe ma con migliaia?
gnasher729,

5

Dalla complessità ciclomatica (Wikipedia):

La complessità ciclomatica di una sezione del codice sorgente è il conteggio del numero di percorsi linearmente indipendenti attraverso il codice sorgente.

  • Ti consiglio di mantenere quel numero sotto i 10 in un unico metodo. Se arriva a 10, allora è il momento di ricodificare.

  • Ci sono strumenti che possono valutare il tuo codice e darti un numero di complessità ciclomatica.

  • Dovresti cercare di integrare questi strumenti nella tua pipeline di build.

  • Non inseguire letteralmente le dimensioni di un metodo, ma cerca di osservarne la complessità e le responsabilità. Se ha più di una responsabilità, allora è probabilmente una buona idea ripensare. Se la sua complessità ciclomatica aumenta, allora è probabilmente il momento di ricodificare.

  • Sono abbastanza certo che ci sono altri strumenti che ti danno un feedback simile, ma non ho ancora avuto modo di esaminarlo.


È stato dimostrato che la complessità ciclomatica, su codice reale proveniente da uno dei grandi repository pubblici, non esempi inventati, è fortemente correlata con lo SLOC grezzo. Ciò lo rende sostanzialmente inutile, poiché è molto più facile contare i ritorni a capo. (Sì, è possibile giocare a SLOC. Sii onesto, qui: per quanto tempo a qualcuno che stava giocando le metriche SLOC sul tuo datore di lavoro avrebbe potuto continuare a disegnare una busta paga?)
John R. Strohm

4

In genere, provo a mantenere i miei metodi / funzioni su ciò che si adatta allo schermo di un monitor 1680x1050. Se non si adatta, utilizzare i metodi / le funzioni di supporto per distribuire l'attività.

Aiuta la leggibilità sia su schermo che su carta.


Faccio la stessa cosa, ma vale la pena specificare quale tipo di carattere e dimensione stai utilizzando. Per quanto mi riguarda, preferisco le "console" con una dimensione di 14, come suggerito da Scott Hanselman. hanselman.com/blog/… È difficile la prima volta lavorare con un font così grande, ma è una buona pratica ricordarti sempre che il tuo metodo dovrebbe essere il più piccolo possibile.
Samuel,

4

Non pongo limiti fissi a nulla perché alcune funzioni implementano algoritmi intrinsecamente complessi e qualsiasi tentativo di renderli più brevi renderebbe le interazioni tra le nuove funzioni più brevi così complicate che il risultato netto non ridurrebbe la semplicità. Inoltre, non credo che l'idea che una funzione debba fare solo "una cosa" è una buona guida, poiché "una cosa" ad un alto livello di astrazione può essere "molte cose" ad un livello inferiore.

Per me, una funzione è decisamente troppo lunga se la sua lunghezza causa subito sottili violazioni di DRY, ed estrarre parte della funzione in una nuova funzione o classe potrebbe risolverlo. Una funzione potrebbe essere troppo lunga se non è così, ma una funzione o una classe potrebbe essere facilmente estratta in modo da rendere il codice più modulare in un modo che potrebbe essere utile di fronte a cambiamenti prevedibili lungo la strada.


Le funzioni fanno "una cosa" a un livello di astrazione specifico e ti preoccupi solo di quel livello di astrazione. Questo è il punto. Se non riesci a capirlo, non penso che tu capisca l'astrazione.
Zoran Pavlovic,

4

Abbastanza corto per essere ottimizzato correttamente

I metodi dovrebbero essere così brevi da fare esattamente una cosa. Il motivo è semplice: in questo modo il tuo codice può essere ottimizzato correttamente.

In un linguaggio JIT come Java o C #, è importante che i metodi siano semplici in modo che il compilatore JIT possa produrre codice rapidamente. Metodi più lunghi e più complicati richiedono naturalmente più tempo JIT. Inoltre, i compilatori JIT offrono solo una manciata di ottimizzazioni e solo i metodi più semplici ne traggono vantaggio. Questo fatto è stato anche messo in evidenza nell'Efficace C # di Bill Wagner .

In un linguaggio di livello inferiore, come C o C ++, anche avere metodi brevi (forse una dozzina di righe) è importante perché in questo modo si minimizza la necessità di memorizzare variabili locali nella RAM piuttosto che in un registro. (Aka 'Register Spilling'.) Si noti tuttavia che in questo caso non gestito, il costo relativo di ciascuna chiamata di funzione può essere piuttosto elevato.

E anche in un linguaggio dinamico, come Ruby o Python, avere metodi brevi aiuta anche nelle ottimizzazioni del compilatore. In un linguaggio dinamico più una caratteristica è "dinamica", più difficile è ottimizzarla. Ad esempio, un metodo lungo che accetta una X e potrebbe restituire un Int, Float o String probabilmente eseguirà molto più lentamente di tre metodi separati che restituiscono ciascuno solo un singolo tipo. Questo perché, se il compilatore sa esattamente quale tipo restituirà la funzione, può anche ottimizzare il sito di chiamata della funzione. (Ad esempio, non verifica la presenza di conversioni di tipo.)


Circa il 99,999% delle applicazioni disponibili ha molte più cose che rallentano la velocità dei programmi come l'accesso al database, l'accesso ai file o la latenza della rete. Pensare alla velocità durante la progettazione dei metodi potrebbe essere un motivo valido per i giochi, l'applicazione in tempo reale o la segnalazione con tonnellate di dati, ma non in altri casi. Tuttavia, è un buon punto, ma piccolo come un programmatore, raramente devo fare questo tipo di ottimizzazione nelle mie applicazioni.
Samuel,

Fai questo genere di cose quando hai misurato la velocità del tuo codice e l'hai trovato troppo lento, se sai cosa stai facendo (non è così semplice come pensi), e se lo misuri in seguito e la velocità è migliorata .
gnasher729,

3

Dipende molto da cosa c'è nel codice.

Ho visto una routine di mille righe con cui non ho avuto problemi. Era un'enorme istruzione switch, nessuna opzione superava una dozzina di righe e l'unica struttura di controllo in ogni opzione era un singolo loop. In questi giorni sarebbe stato scritto con oggetti ma non era un'opzione allora.

Sto anche guardando 120 linee in un interruttore di fronte a me. Nessun caso supera 3 righe: una guardia, un incarico e l'interruzione. Sta analizzando un file di testo, gli oggetti non sono una possibilità. Qualsiasi alternativa sarebbe più difficile da leggere.


2

Alla maggior parte dei compilatori non importa la lunghezza di una funzione. Una funzione dovrebbe essere funzionale, ma essere sia facile da capire, cambiare e riutilizzare per gli esseri umani. Scegli una lunghezza adatta a te.


1

La mia regola generale è che una funzione dovrebbe adattarsi allo schermo. Sono stati trovati solo tre casi che tendono a violare:

1) Funzioni di invio. Ai vecchi tempi questi erano comuni ma la maggior parte di questi sono sostituiti con l'eredità degli oggetti in questi giorni. Gli oggetti funzionano solo all'interno del tuo programma, quindi vedrai comunque funzioni di invio occasionali quando gestisci i dati che arrivano da altrove.

2) Funzioni che eseguono tutta una serie di passaggi per raggiungere un obiettivo e in cui i passaggi mancano di una buona suddivisione. Si finisce con una funzione che chiama semplicemente un lungo elenco di altre funzioni in ordine.

3) Come il n. 2 ma in cui i singoli passaggi sono così piccoli da essere semplicemente allineati anziché chiamati separatamente.


1

Forse la lunghezza della funzione non è una buona metrica. Cerchiamo di usare la complessità ciclomatica , anche su metodi, e uno dei futuri controlli del codice sorgente controlla che la complessità ciclomatica su classi e metodi deve essere inferiore a X.

Per i metodi, X è impostato su 30 ed è abbastanza stretto.

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.