Quanti parametri sono troppi? [chiuso]


228

Le routine possono avere parametri, questa non è una novità. Puoi definire tutti i parametri di cui potresti aver bisogno, ma troppi di questi renderanno la tua routine difficile da capire e mantenere.

Ovviamente, potresti usare una variabile strutturata come soluzione alternativa: mettere tutte quelle variabili in una singola struttura e passarle alla routine. In effetti, l'uso delle strutture per semplificare gli elenchi di parametri è una delle tecniche descritte da Steve McConnell in Code Complete . Ma come dice:

I programmatori attenti evitano di raggruppare i dati più di quanto non sia logicamente necessario.

Quindi se la tua routine ha troppi parametri o usi una struttura per mascherare un grande elenco di parametri, probabilmente stai facendo qualcosa di sbagliato. Cioè, non stai mantenendo l'accoppiamento allentato.

La mia domanda è: quando posso considerare un elenco di parametri troppo grande? Penso che oltre 5 parametri siano troppi. Cosa ne pensi?


1
Basta fare riferimento qui a questa domanda che è un esempio pratico di quanti parametri sono troppi ...
Gideon

5
In JavaScript 65537 i parametri sono troppi: jsfiddle.net/vhmgLkdm
Aadit M Shah

guarda i componenti http di apache è quasi impossibile costruirne qualcosa di dinamico
Andrew Scott Evans,

Risposte:


162

Quando una cosa è considerata così oscena da essere qualcosa che può essere regolata nonostante il 1 ° emendamento garantisca la libertà di parola? Secondo il giudice Potter Stewart, "lo so quando lo vedo". Lo stesso vale qui.

Odio fare regole dure e veloci come questa perché la risposta cambia non solo in base alle dimensioni e alla portata del tuo progetto, ma penso che cambi anche fino al livello del modulo. A seconda di ciò che sta facendo il tuo metodo o di ciò che la classe dovrebbe rappresentare, è possibile che 2 argomenti siano troppi ed è un sintomo di troppo accoppiamento.

Suggerirei che ponendo la domanda in primo luogo, e qualificando la tua domanda tanto quanto te, che tu sappia davvero tutto questo. La soluzione migliore qui non è quella di fare affidamento su un numero forte e veloce, ma piuttosto guardare alle revisioni del progetto e alle revisioni del codice tra i tuoi pari per identificare le aree in cui hai bassa coesione e accoppiamento stretto.

Non aver mai paura di mostrare ai tuoi colleghi il tuo lavoro. Se hai paura, questo è probabilmente il segno più grande che qualcosa non va nel tuo codice e che già lo conosci .


Una buona regola empirica è il numero di registri della CPU, perché più e il compilatore saranno costretti a allocarli nello stack.
Michaelangel007

1
@ Michaelangel007 riguardo alla risposta che stai commentando non dovresti dirlo, perché dice che non esiste una regola. Inoltre, il numero di parametri è una questione di leggibilità, non di prestazioni.
clemp6r

1
@ clemp6r Inesatto - è entrambi . I compilatori devono fare i conti con la "fuoriuscita del registro" in ogni momento. L' unica risposta autorevole è ispezionare l'assemblaggio di ciò che il tuo compilatore sta generando. Il numero di registri non cambia magicamente sulla stessa piattaforma. it.wikipedia.org/wiki/Register_allocation
Michaelangel007

124

Una funzione può avere troppi parametri solo se alcuni dei parametri sono ridondanti. Se vengono utilizzati tutti i parametri, la funzione deve avere il numero corretto di parametri. Prendi questa funzione spesso usata:

HWND CreateWindowEx
(
  DWORD dwExStyle,
  LPCTSTR lpClassName,
  LPCTSTR lpWindowName,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hWndParent,
  HMENU hMenu,
  HINSTANCE hInstance,
  LPVOID lpParam
);

Sono 12 parametri (9 se si raggruppano x, y, w e h come un rettangolo) e ci sono anche i parametri derivati ​​dal nome della classe. Come riduresti questo? Vorresti ridurre il numero di più al punto?

Non lasciare che il numero di parametri ti dia fastidio, assicurati solo che sia logico e ben documentato e che intellisense * ti aiuti.

* Sono disponibili altri assistenti di codifica!


37
Sto votando questo. Sono stupito di tutte le altre risposte che dicono "3" e "4"! La risposta corretta è: il minimo necessario, che a volte può essere un bel po '.
Tony Andrews,

16
Se quella funzione fosse stata progettata oggi, sarebbe probabilmente un po 'diversa. x, y, nWidth e nHeight possono essere raggruppati in un oggetto Rectangle. style e xStyle potrebbero essere combinati in un insieme di enumerazioni o stringhe. Ora hai solo 8 parametri.
finnw,

16
@finnw Se quella funzione fosse stata progettata oggi? È già stato riprogettato. Form f = new Form (); ha 0 parametri.
Nick,

36
Questo è C e Winapi. Non riesco a pensare a un esempio peggiore.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳,

17
No Questo è stile procedurale. Le finestre sono oggetti, quindi ora è obsoleto. Oggi, questo sarebbe risolto con classi aggregate, non con un mucchio di parametri. La regola intuitiva del buon design è che se non riesci a descrivere la funzione (compresi i parametri) in una semplice frase, è mal progettata . Credo che sia così.
Jan Turoň,

106

In Clean Code , Robert C. Martin ha dedicato quattro pagine all'argomento. Ecco l'essenza:

Il numero ideale di argomenti per una funzione è zero (niladic). Ne segue uno (monadico), seguito da vicino da due (diadico). Tre argomenti (triadici) dovrebbero essere evitati ove possibile. Più di tre (poliadici) richiedono una giustificazione molto speciale - e quindi non dovrebbero essere usati comunque.


2
Dubito che includa "questo" perché quello è il contesto dell'esecuzione. Nella programmazione funzionale, il contesto è globale e in oop, il contesto è l'oggetto su cui si sta applicando il metodo. Inoltre, se includessi "this" nell'elenco dei parametri, sarebbe impossibile avere 0 params (ideale).
Tom,

1
No, non include "questo".
Patrick McElhaney,

20
Inoltre, Martin dovrebbe parlare con il comitato standard C ++. La metà di <algoritmo> accetta più di 3 parametri, perché solo un intervallo di iteratori è già 2. È solo la natura della programmazione con iteratori (ovvero programmazione generica con raccolte STL).
Steve Jessop,

3
@SteveJessop: l'errore di <algorithm> è che funziona sempre su una slice. Se l'algoritmo e la raccolta dovessero essere riprogettati, farei in modo che gli algoritmi prendano sempre un'intera raccolta e otterresti un modo per suddividere una vista di una raccolta per consentire agli algoritmi di lavorare sulle parti; in alternativa definirei anche una sostituzione abbreviata per semplificare il lavoro sul caso comune.
Lie Ryan,

3
@LieRyan: mentre se dovessi ridisegnare <algorithm>lo farei agire su un oggetto range. Le raccolte sarebbero intervalli, ma non tutte le gamme sarebbero raccolte. E in effetti, la spinta lo ha già fatto. Ad ogni modo, il mio punto è che una biblioteca ampiamente utilizzata ignora questo consiglio, quindi il peggio che ti è garantito se lo fai anche tu è che molti dei tuoi milioni di utenti armeggeranno con piccole semplificazioni alla tua interfaccia ;-)
Steve Jessop

79

Alcuni codici con cui ho lavorato in passato utilizzavano variabili globali solo per evitare di passare troppi parametri in giro.

Per favore, non farlo!

(Generalmente.)


Alcuni codici su cui stavo lavorando usavano i membri della classe per ottenere la stessa cosa. A quel tempo ero un programmatore C e ho chiesto, perché ci sono così tante variabili globali, perché input / output non possono essere parametri espliciti?
user3528438

38

Se inizi a dover contare mentalmente i parametri nella firma e abbinarli alla chiamata, allora è il momento di rifattorizzare!


Questa è un'ottima risposta. Se i parametri sono organizzati logicamente (x, y, w, h) è facile ricordarli tutti nell'ordine corretto. È più difficile ricordare dove mettere il puntatore FILE in putc (che ha solo due parametri), soprattutto perché fprintf è l'opposto.
user877329,

Risposta più bella Molto ben detto
Anwar,

31

Grazie mille per tutte le tue risposte:

  • È stato un po 'sorprendente trovare persone che pensano (come faccio io) che 5 parametri siano un buon limite per la sanità mentale del codice.

  • Generalmente, le persone tendono a concordare sul fatto che un limite tra 3 e 4 è una buona regola empirica. Questo è ragionevole poiché le persone di solito si divertono a contare più di 4 cose.

  • Come indica Milano , in media le persone possono tenere in mente più o meno 7 cose alla volta. Ma penso che non si possa dimenticare che, quando si progetta / mantiene / studia una routine, bisogna tenere a mente più cose che solo i parametri.

  • Alcune persone ritengono che una routine dovrebbe avere tutti gli argomenti di cui ha bisogno. Sono d'accordo, ma solo per alcuni casi specifici (chiamate alle API del sistema operativo, routine in cui l'ottimizzazione è importante, ecc.). Suggerisco di nascondere la complessità di queste routine aggiungendo uno strato di astrazione appena sopra queste chiamate ogni volta che è possibile.

  • Nick ha alcuni pensieri interessanti su questo. Se non vuoi leggere i suoi commenti, ti riassumo per te: in poche parole, dipende :

    Odio fare regole dure e veloci come questa perché la risposta cambia non solo in base alle dimensioni e alla portata del tuo progetto, ma penso che cambi anche fino al livello del modulo. A seconda di ciò che sta facendo il tuo metodo o di ciò che la classe dovrebbe rappresentare, è possibile che 2 argomenti siano troppi ed è un sintomo di troppo accoppiamento.

    La morale qui non ha paura di mostrare il tuo codice ai tuoi coetanei, discuti con loro e cerca di "identificare le aree in cui hai bassa coesione e stretto accoppiamento" .

  • Infine, penso che lo stordimento sia molto d'accordo con Nick, e conclude il suo contributo satirico con questa visione poetica (vedi commenti sotto) dell'arte della programmazione:

    La programmazione non è ingegneria. L'organizzazione del codice è un'arte perché dipende da fattori umani, che dipendono troppo dal contesto per qualsiasi regola rigida.


16

Questa risposta assume una lingua OO. Se non ne usi uno, salta questa risposta (in altre parole questa non è una risposta agnostica al linguaggio).

Se stai passando più di 3 o più parametri (in particolare tipi / oggetti intrinseci), non è "Troppi", ma potresti non avere la possibilità di creare un nuovo oggetto.

Cerca gruppi di parametri che vengono passati in più di un metodo - anche un gruppo passato in due metodi garantisce quasi che dovresti avere un nuovo oggetto lì.

Quindi rifletti la funzionalità nel tuo nuovo oggetto e non crederesti quanto aiuti sia il tuo codice che la tua comprensione della programmazione OO.


Per favore, dai un nome a una lingua che non usi routine né parametri. Non riesco a pensarne uno, anche l'assemblaggio potrebbe essere considerato come routine (etichette).
Auron,

L'assembly x86 ha sicuramente delle routine (call / return). Altri potrebbero richiedere combo cmp / jmp.
Brian Knoblauch,

Siamo spiacenti, "ret", non "return". Sospiri. È già stata una lunga giornata.
Brian Knoblauch,

Mi dispiace per l'ambiguo fraseggio Auron, credo di averlo corretto. Era la mia risposta specifica della lingua, non la domanda.
Bill K,

1
Non tutte le lingue consentono il passaggio di strutture. Inoltre, il passaggio di una struttura significa che il metodo di ricezione deve avere il codice per gestire la struttura e potrebbe quindi necessitare di più parametri. Inoltre, in un linguaggio non oo, in genere è necessario un parametro aggiuntivo: tutti i linguaggi OO hanno un parametro "Nascosto" di "questo" che altrimenti dovrebbe essere passato (o, in un linguaggio / design più orribile, potrebbe essere a livello globale accessibile)
Bill K,

13

Sembra che ci siano altre considerazioni oltre al semplice numero, eccone alcune che mi vengono in mente:

  1. relazione logica con lo scopo principale della funzione rispetto alle impostazioni una tantum

  2. Se sono solo flag di ambiente, il raggruppamento può essere molto utile


12

Uno dei più noti epigrammi di programmazione di Alan Perlis (raccontato in ACM SIGPLAN Avvisi 17 (9), settembre 1982, 1982) afferma che "Se hai una procedura con 10 parametri, probabilmente ne hai perso alcuni".



9

Per me, quando l'elenco incrocia una riga sul mio IDE, allora è un parametro di troppo. Voglio vedere tutti i parametri in una riga senza interrompere il contatto visivo. Ma questa è solo la mia preferenza personale.


Questo va bene fino a quando con alcuni sviluppatori non proverai a usare foo (int a, float b, stringa c, double d). Meglio cercare di evitare di lavorare con loro, credo. : D
Rontologo,

4
Se qualcun altro ha dato alle classi nomi ridicolmente lunghi non lascerei che influenzasse il modo in cui definisco o chiamo le mie routine.
finnw,

9
Posso presentarti il ​​"ritorno a capo"?

3
Quando l'elenco attraversa una riga sul tuo IDE, il tuo monitor è troppo piccolo per usarlo con quel codice e dovresti chiaramente acquistare un monitor con una risoluzione orizzontale più alta. Un'interruzione di riga o una riduzione della quantità di parametri sono solo soluzioni alternative che non risolvono il problema di root, ovvero che il monitor è troppo piccolo per quella base di codice!
Kaiserludi,

9

Sono generalmente d'accordo con 5, tuttavia, se c'è una situazione in cui ho bisogno di più ed è il modo più chiaro per risolvere il problema, allora userei di più.


8

Sette cose nella memoria a breve termine?

  1. Nome della funzione
  2. Restituisce il valore della funzione
  3. Scopo della funzione
  4. Parametro 1
  5. Parametro 2
  6. Parametro 3
  7. Parametro 4

Bene. È una regola empirica. Quando stai codificando il corpo di una funzione, non ti interessa il suo nome e il valore di ritorno e il suo scopo sono strettamente correlati.
Auron,

7
8. ordine dei parametri
Eva,

7

Nei peggiori 5 frammenti di codice , controlla il secondo, "È questo un costruttore". Ha circa 37 ⋅ 4 ≈ 150 parametri:

Qui un programmatore ha scritto questo costruttore [... S] qualcuno di voi potrebbe pensare di sì, è un grande costruttore, ma ha usato gli strumenti di generazione automatica del codice di eclissi [.] NOO, in questo costruttore c'era un piccolo bug che ho scoperto, che mi ha fatto Concludo che questo costruttore è stato scritto a mano. (a proposito, questa è solo la parte superiore del costruttore, non è completa).

costruttore con oltre 150 parametri


Questo è così triste ... Non conoscere "record" o "strutture" o "oggetti valore", che raggruppano diversi valori e danno loro un nome comune in modo da poter rappresentare gerarchicamente molti di loro in un modo leggibile è come camminare in giro con le scarpe allentate per anni perché nessuno ti ha mai detto che potresti persino farlo 🙈
yeoman,

6

Uno in più del necessario. Non intendo essere glib, ma ci sono alcune funzioni che necessitano necessariamente di alcune opzioni. Per esempio:

void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset);

Ci sono 6 argomenti, e ognuno di essi è essenziale. Inoltre, non esiste un collegamento comune tra loro per giustificare il raggruppamento. Forse potresti definire "struct mmapargs", ma sarebbe peggio.


Bene, prote flagsavrebbe potuto essere messo insieme se il designer avesse ritenuto che 5 fosse in qualche modo un numero magico molto migliore di 6. Un po 'come il modo in cui si opencombina la modalità di lettura / scrittura con tutte le altre bandiere varie. E forse si potrebbe sbarazzarsi di offsetspecificando che la sezione inizia mappati al corrente cercano posizione filedes. Non so se ci sono situazioni in cui è possibile creare mmapuna regione in cui non si è in grado di lseekfarlo, ma in caso contrario non è strettamente necessario.
Steve Jessop,

... quindi penso che mmapsia una buona illustrazione del fatto che alcuni progettisti e utenti preferiscono un lungo elenco di parametri, mentre altri preferiscono passare alcuni passaggi preparando un numero inferiore di parametri prima di effettuare la chiamata.
Steve Jessop,

1
@Steve: impostare anticipatamente la posizione di ricerca con una chiamata separata avrebbe introdotto una condizione di gara gratuita. Per alcune API (OpenGL), ci sono così tanti parametri che influenzano una chiamata che devi davvero usare lo stato, ma normalmente ogni chiamata dovrebbe essere il più possibile isolata. L'azione a distanza è il percorso verso il lato oscuro.
Ben Voigt,

5

Secondo Perl Best Practices , 3 va bene, 4 è troppa. È solo una linea guida, ma nel nostro negozio è ciò a cui cerchiamo di attenerci.


5

Disegnerei il limite per le funzioni pubbliche con 5 parametri.

IMHO, gli elenchi di parametri lunghi sono accettabili solo nelle funzioni di supporto private / locali che devono essere richiamate solo da alcuni punti specifici del codice. In questi casi, potrebbe essere necessario trasmettere molte informazioni statali, ma la leggibilità non è così importante poiché solo tu (o qualcuno che manterrà il tuo codice e dovrebbe comprendere i fondamenti del tuo modulo) devi preoccuparti chiamando quella funzione.


Il che è esattamente il motivo per cui molte persone a questo punto farebbero di tutto ciò che non è un vero argomento, ma lo stato portato in giro, solo lo stato di un oggetto sotto forma di campi (variabili membro). Quell'oggetto sarebbe rappresentato come una classe. Sarebbe istanziato una volta o per ogni uso. A volte un tale oggetto avrà solo un pacchetto privato o metodo pubblico, che quindi delegherà il lavoro a metodi privati ​​con pochi argomenti ciascuno. Pochi argomenti sono ora possibili perché la configurazione e lo stato ora hanno un posto adeguato :)
yeoman

5

Una domanda correlata che dovresti prendere in considerazione è quanto sia coerente la routine. Un gran numero di parametri può essere un odore che ti dice che la routine stessa sta provando a fare troppo e quindi la sua coesione è sospetta. Concordo sul fatto che un numero duro e veloce di parametri sia probabilmente impossibile, ma immagino che una routine di alta coesione implicherebbe un basso numero di parametri.



4

Mi fermo a tre parametri come regola generale. Ancora ed è tempo di passare invece una matrice di parametri o un oggetto di configurazione, che consente anche l'aggiunta di parametri futuri senza modificare l'API.


Se l'API cambia, allora l'API dovrebbe effettivamente cambiare, non solo avere una modifica invisibile in cui l'incompatibilità potrebbe ancora verificarsi, ma essere meno ovvia.
wnoise,

tuttavia, se è necessario un altro parametro per la configurazione di un caso limite, non dovrebbe propagarsi a componenti non correlati utilizzando l'API
Eran Galperin,

4

Una restrizione di lunghezza su un elenco di parametri è solo un'altra limitazione. E restrizione significa violenza applicata. Sembra divertente, ma puoi essere non violento anche durante la programmazione. Lascia che il codice imponga le regole. È ovvio che se hai molti parametri, il corpo del metodo funzione / classe sarà abbastanza grande da usarli. E i frammenti di codice di grandi dimensioni di solito possono essere rifattorizzati e suddivisi in blocchi più piccoli. Quindi ottieni la soluzione contro avere molti parametri come bonus gratuito, poiché sono suddivisi tra i pezzi di codice refactored più piccoli.


4

Una cosa che vorrei sottolineare dal punto di vista delle prestazioni è che, a seconda di come si passano i parametri a un metodo, passare molti parametri in base al valore rallenterà il programma perché ogni parametro deve essere copiato e quindi posto nello stack.

L'uso di una singola classe per comprendere tutti i parametri funzionerebbe meglio perché un singolo parametro passato per riferimento sarebbe elegante, più pulito e più veloce!


Sto dando a questa risposta un +1 perché è l'unico che discute qualsiasi cosa oltre alla pulizia del codice o ai limiti arbitrari, che sono entrambi soggettivi. Alcune persone potrebbero non pensare a cosa stai facendo nello stack quando sei in loop e spingi dozzine di argomenti dentro e fuori dallo stack. Se è in un ciclo, dovresti prendere in considerazione l'utilizzo del numero di argomenti passati da REGISTER nell'ABI per cui stai compilando. Ad esempio, nell'ABI MS x64, l'args massimo passato attraverso i registri è 4. L'ABI "System V" (utilizzato da sistemi operativi non Windows) utilizza più registri, quindi l'utilizzo di 4 args è abbastanza portatile
Lakey,

3

Secondo me potrebbero esserci casi in cui supererai 4 o qualche numero fisso. Le cose da guardare potrebbero essere

  1. Il tuo metodo sta facendo troppo e devi rifattorizzare.
  2. Si consiglia di utilizzare una raccolta o una struttura di dati.
  3. Ripensare il design della tua classe, forse alcune cose non devono essere passate in giro.

Dal punto di vista della facilità d'uso o della facilità di lettura del codice, penso che quando è necessario "racchiudere in una parola" la firma del metodo, ciò dovrebbe farti fermare e pensare, a meno che non ti senta impotente e tutti gli sforzi per rendere la firma più piccola conducano a nessun risultato. Alcune librerie molto buone in passato e presente usano più di 4-5 carrozzine.


3

La mia regola empirica è che devo essere in grado di ricordare i parametri abbastanza a lungo per guardare una chiamata e dire cosa fa. Quindi se non riesco a guardare il metodo e poi passare a una chiamata di un metodo e ricordare quale parametro fa quello, allora ce ne sono troppi.

Per me questo equivale a circa 5, ma non sono così brillante. Il tuo chilometraggio può variare.

È possibile creare un oggetto con proprietà per contenere i parametri e passarlo se si supera il limite impostato. Vedi il libro Refactoring di Martin Fowler e il capitolo su come rendere più semplici le chiamate di metodo.


1

Dipende fortemente dall'ambiente in cui stai lavorando. Prendi ad esempio javascript. In javascript il modo migliore per passare parametri è usare oggetti con coppie chiave / valore, che in pratica significa che hai solo un parametro. In altri sistemi il punto debole sarà a tre o quattro.

Alla fine, tutto si riduce al gusto personale.


1

Concordo con 3 va bene, 4 è troppa come linea guida. Con più di 3 parametri, stai inevitabilmente facendo più di un compito. Più di un'attività dovrebbe essere suddivisa in metodi separati.

Tuttavia, se guardassi l'ultimo progetto a cui ho lavorato, le eccezioni abbonderebbero e la maggior parte dei casi sarebbe difficile scendere a 3 parametri.


1

Se ho 7-10 parametri in una routine guardo a raggrupparli in una nuova classe ma non se quella classe non sarebbe altro che un mucchio di campi con getter e setter - la nuova classe deve fare qualcosa di diverso dai valori shuffle in e su. Altrimenti preferirei sopportare la lunga lista di parametri.


1
Lo raggrupperò con una classe di soli dati se viene utilizzato in più di un posto, ma anche in questo caso di solito creo entrambi i costruttori.
Leahn Novash,

1

È noto che, in media, le persone possono tenere in testa 7 +/- 2 cose alla volta. Mi piace usare quel principio con parametri. Supponendo che i programmatori siano tutti persone intelligenti al di sopra della media, direi che tutto 10+ sono troppi.

A proposito, se i parametri sono simili in qualche modo, li metterei in un vettore o in una lista piuttosto che in una struttura o in una classe.


1

Baserei la mia risposta sulla frequenza con cui viene chiamata la funzione.

Se è una funzione init che viene chiamata una sola volta, allora lascia che prenda 10 parms o più, a chi importa.

Se viene chiamato un sacco di volte per fotogramma, tendo a creare una struttura e semplicemente a passare un puntatore ad essa poiché tende ad essere più veloce (supponendo che non si stia ricostruendo anche la struttura ogni volta).


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.