Qual è la migliore pratica per ordinare i parametri in una funzione?


52

A volte (raramente), sembra che creare una funzione che accetta una discreta quantità di parametri sia la strada migliore. Tuttavia, quando lo faccio, mi sento come se stessi spesso scegliendo l'ordine dei parametri a caso. Di solito vado per "ordine di importanza", prima con il parametro più importante.

C'è un modo migliore per farlo? Esiste un modo "best practice" per ordinare i parametri che migliora la chiarezza?


5
I parametri nominati lo rendono meno di un problema. Python lo porta all'estremo, guardalo dentro. Un buon esempio è in C #: i molti modi per chiamare MessageBox.Show. Guarda anche in quello.
Giobbe

8
In Haskell, la possibile utilità dell'applicazione di funzioni parziali di solito suggerisce un ordinamento naturale degli argomenti.
martedì

Cosa considereresti una discreta quantità di parametri?
Chris,

Stavo pensando> 2. Anche se immagino che l'ordinamento si applichi allo stesso modo a 2 parametri.
Casey Patton,

3
@tdammers questo vale anche per qualsiasi lingua che abbia un supporto diretto per curry
jk.

Risposte:


55

In generale: usalo .

Scrivi un test per la tua funzione, un test del mondo reale.
Qualcosa che vorresti davvero fare con quella funzione .

E vedi in quale ordine hai messo giù quelli.

A meno che tu non abbia già (o sappia) alcune funzioni che fanno qualcosa di simile.
In tal caso: conformarsi a ciò che già fanno, almeno per i primi argomenti.

ad esempio, prendono tutti un documento / oggetto / file-pointer / serie di valori / coordinate come i primi argomenti? Per l'amor di Dio, conformati a quegli argomenti .

Evita di confondere i tuoi colleghi e il tuo io futuro .


12
"Evita di confondere ... il tuo io futuro" suona come una buona filosofia in generale.
Jeanne Pindar,

28

Di solito vado con queste regole, anche se non sempre con la stessa precedenza. Immagino che ora sia un processo di pensiero automatico e non ci penso troppo, tranne che per la progettazione di API pubbliche .

Imbuto di selezione

  1. Semantica
  2. Importanza / Rilevanza
  3. Frequenza d'uso
  4. Preoccupazioni I / O

1. Prima la semantica

Soprattutto in OOP, seleziona i parametri in base al loro significato semantico per l'azione o il messaggio. La firma di un metodo ben definito con un parametro ben definito dovrebbe:

  • sentire naturale chiamare,
  • essere auto-descrittivo in termini di intenti e comportamenti.

(Per questi motivi, a volte l'utilizzo di tipi o alias personalizzati anziché primitivi potrebbe aumentare l'espressività della firma.)

2. Quindi importanza

Il parametro più "significativo" viene prima (o dopo ...)

3. Quindi frequenza

Anche la frequenza è importante, specialmente in una lingua in cui non si hanno parametri nominati ma si possono avere valori predefiniti sui parametri posizionali. Ciò implica che l'ordine dei parametri non varia e che ovviamente non è possibile impostare i parametri N + 1 se si desidera forzare il valore predefinito dell'ennesimo parametro (tranne se la propria lingua ha un concetto di parametro segnaposto ).

La buona notizia per te è che di solito la frequenza riguarda l'importanza, quindi va di pari passo con il punto precedente. E quindi probabilmente spetta a te creare la tua API affinché abbia la semantica appropriata.

4. Non dimentichiamo l'I / O

se il tuo metodo / funzione accetta alcuni input e produce un output, e quest'ultimo non deve essere "restituito" (tramite un'istruzione return) o "generato" (utilizzando un sistema di eccezione), allora hai la possibilità di passare valori al chiamante utilizzando gli altri parametri (o il parametro di input). Ciò si riferisce alla semantica e nella maggior parte dei casi ha senso che i primi parametri definiscano l'output e gli ultimi parametri ricevano l'output.

Inoltre, un altro approccio per avere meno parametri e massimizzare la semantica sarebbe usare un approccio funzionale o definire un modello Builder , in modo da poter impilare chiaramente i tuoi input, definire i tuoi output e recuperarli quando necessario.

(Nota che non menziono le variabili globali, perché perché dovresti usarne una, giusto?)


Alcune cose da considerare

  • usabilità

    La maggior parte di quanto sopra mostrerà naturalmente se segui i consigli di ZJR : Usalo!

  • Prendi in considerazione il refactoring

    Se ti preoccupi dell'ordinamento dei parametri, forse questa preoccupazione trova la sua radice in quanto sopra e nel fatto che la tua API è mal progettata. Se hai troppi parametri, molto probabilmente qualcosa può essere componentizzato / modulare e refactored .

  • Prendi in considerazione le prestazioni

    Tieni presente che le implementazioni di alcune lingue comporteranno impatti molto importanti sulla gestione della memoria di runtime durante l'utilizzo dei parametri. Da qui il motivo per cui i libri di stile di molte lingue raccomandano di mantenere l'elenco dei parametri semplice e breve . Ad un massimo di 4 parametri, ad esempio. Lascio che sia un esercizio per te capire perché.

  • Anche la risposta di Bevan e la menzione delle raccomandazioni di Clean Code sono decisamente rilevanti!


18

Sottolineo rispettosamente che preoccuparsi dell'ordinamento dei parametri è preoccuparsi della cosa sbagliata.

Nel libro sullo zio Bob " Clean Code " sostiene, in modo persuasivo, che i metodi non dovrebbero mai avere più di due argomenti - e la maggior parte dovrebbe avere solo uno, se presente. In questo caso, l'ordinamento è ovvio o non importante.

Per quanto imperfetto, sto cercando di seguire il consiglio di zio Bob - e sta migliorando il mio codice.

Nei rari casi in cui un metodo sembra richiedere più informazioni, l'introduzione di un oggetto parametro è una buona idea. Di solito, trovo che questo sia il primo passo verso la scoperta di un nuovo concetto (oggetto) che è la chiave del mio algoritmo.


Sono assolutamente d'accordo con te! La mia prima affermazione mirava ad arrivare al punto che questa non è una cosa tipica per me, ahah. Ma nel caso in cui trovassi che ho davvero bisogno di passare alcuni parametri e non vale la pena refactificare l'intero programma, mi chiedo come ordinare.
Casey Patton,

3
tosse PHP tosse . Mi dispiace - stavi dicendo qualcosa sul non preoccuparti dell'ordine dei parametri?
Craige,

Non hai quindi solo un costruttore per l'oggetto parametro che accetta altrettanti argomenti?
Detly

No, perché l'oggetto parametro può essere "costruito" su più istruzioni in un modo più leggibile.
Bevan

2
@Bevan: questo è altamente discutibile. Costruire oggetti in questo modo significa che non possono più essere immutabili; mantenere i tuoi oggetti immutabili quando possibile, tuttavia è un altro ottimo modo per aumentare la manutenibilità.
Martedì

10

Provo a mettere prima i parametri IN, poi i parametri OUT. Ci sono anche alcuni ordinamenti naturali, ad esempio createPoint(double x, double y)è fortemente preferibile createPoint(double y, double x).


5
A volte è meglio il contrario, ad esempio, quando c'è sempre solo un argomento OUT e un numero variabile di argomenti in, come in addAllTo(target, something1, something2).
maaartinus,

Sebbene segua la stessa regola, cerco di evitare i parametri OUT ogni volta che è possibile, e anche i programmatori più bravi. Quindi il numero di funzioni in cui questa raccomandazione aiuta effettivamente il PO dovrebbe essere piuttosto piccolo.
Doc Brown,

7
@DocBrown Non dovresti davvero cercare di evitare buoni programmatori, possono essere utili avere in giro.
RyanfaeScotland,

@RyanfaeScotland: accidenti, dovrei leggere i miei commenti due volte prima di pubblicarli (o almeno entro cinque minuti posso cambiarli) ;-)
Doc Brown,

6

Non ho mai visto una "best practice" documentata relativa a questo particolare argomento, ma il mio standard personale è elencarli nell'ordine in cui appariranno nel metodo per il quale vengono utilizzati o se il metodo è più di un passando a un livello dati, li elencherò nell'ordine in cui apparirebbero nello schema db o nei metodi del livello dati.

Inoltre, quando ci sono più sovraccarichi di un metodo, noto che il modo tipico è elencarli a partire da parametri comuni a tutti (o alla maggior parte) dei metodi con ciascun metodo diverso aggiunto alla fine per ogni sovraccarico di metodo come :

void func1(string param) { }
void func2(string param, int param2) { }
void func3(string param, string param3) { }
void func3(string param, int param2, string param3) { }


3

Seguo spesso la convenzione C / C ++ di posizionare constprima i parametri (ovvero i parametri che passi per valore) e poi quelli che passi per riferimento. Questo potrebbe non essere necessariamente il metodo corretto per chiamare le funzioni ma, se sei interessato a come ciascun compilatore gestisce i parametri, dai un'occhiata ai seguenti collegamenti per le regole che governano e / o l'ordine in cui i parametri vengono inseriti nello stack.

http://msdn.microsoft.com/en-us/library/zthk2dkh%28v=vs.80%29.aspx

http://en.wikipedia.org/wiki/Calling_convention


Un altro collegamento che potrebbe interessarti è msdn.microsoft.com/en-us/library/984x0h58%28v=vs.71%29.aspx Per le funzioni ricorsive, dai un'occhiata al seguente link. publications.gbdirect.co.uk/c_book/chapter4/… Non che me ne fossi dimenticato ma, essendo un nuovo utente, non sono in grado di pubblicare un commento che ha più di due link .... che frustrante!
Bill,

1

Di solito vado solo con l'ordinamento dei parametri "che sembra meno criptico". Meno volte ho bisogno di andare alla definizione del metodo / funzione, meglio è. Ed è bello avere parametri nominativi che descrivono a cosa servono, in questo modo quando viene visualizzata la piccola descrizione comandi (VS), allora è ancora più semplice.

Se hai linee e linee di parametri potresti voler prendere in considerazione un design diverso. Fai un passo indietro e scopri come suddividerlo in più funzioni / metodi. Solo un'idea, ma quando ho una dozzina di parametri nella mia funzione non è quasi sempre un problema di parametri, ma un problema di progettazione.


2
"cyprtic": un ottimo modo per chiarire il punto.
Bevan,

2
Non hai mai avuto un refuso prima, @Bevan?

1
Ho sempre errori di battitura - scrivere cyprtic è stato così bello che ho pensato che fosse apposta . Ti preghiamo di cambiarlo di nuovo, aiuta a chiarire il tuo punto, anche se è stato un incidente.
Bevan,

1
@Bevan, haha ​​okay capisco cosa stai dicendo. Buon punto! È tornato indietro =)

1

A volte (raramente), sembra che creare una funzione che accetta una discreta quantità di parametri sia la strada migliore.

L'uso di più parametri è spesso un chiaro indicatore della violazione dell'SRP in questo metodo. È improbabile che un metodo, che richiede molti parametri, faccia solo una cosa. L'esclusione può essere una funzione matematica o un metodo di configurazione, in cui sono effettivamente necessari diversi parametri in quanto tali. Eviterei più parametri poiché il diavolo evita l'acqua santa. Più parametri si utilizzano all'interno di un metodo, maggiore è la possibilità che il metodo sia (troppo) complesso; più complessità significa: più difficile da mantenere e meno desiderabile.

Tuttavia, quando lo faccio, mi sento come se stessi spesso scegliendo l'ordine dei parametri a caso. Di solito vado per "ordine di importanza", prima con il parametro più importante.

In linea di principio stai scegliendo a caso . Naturalmente potresti pensare che il parametro A sia più rilevante del parametro B ; ma potrebbe non essere il caso degli utenti della tua API, che pensano che B sia il parametro più rilevante. Quindi, anche se sei stato attento nella scelta dell'ordine, per altri potrebbe sembrare casuale .

C'è un modo migliore per farlo? Esiste un modo "best practice" per ordinare i parametri che migliora la chiarezza?

Esistono diversi modi:

a) Il caso banale: non utilizzare più di un parametro.

b) Dato che non hai specificato quale lingua hai scelto, c'è la possibilità che tu abbia scelto una lingua con parametri nominati . Questo è un bel zucchero sintattico che ti permette di allentare il significato dell'ordine dei parametri:fn(name:"John Doe", age:36)

Non tutte le lingue consentono tali prelibatezze. E allora?

c) È possibile utilizzare un dizionario / Hashmap / matrice associativa come parametro: ad es. Javascript consentirebbe quanto segue: fn({"name":"John Doe", age:36})che non è lontano da (b).

d) Naturalmente se lavori con un linguaggio tipicamente statico come Java. potresti usare una hashmap , ma perderai le informazioni sui tipi (ad es. quando lavori con HashMap<String, Object>) quando i parametri hanno tipi diversi (e devi lanciarli).

Il prossimo passo logico sarebbe passare un Object(se stai usando Java) con proprietà appropriate o qualcosa di più leggero come uno struct (se scrivi ad esempio C # o C / C ++).

Regola del pollice:

1) Il caso migliore: il tuo metodo non ha bisogno di alcun parametro

2) Buon caso - il tuo metodo ha bisogno di un parametro

3) Caso tollerabile: il metodo richiede due parametri

4) Tutti gli altri casi devono essere sottoposti a refactoring


0

Spesso un oggetto complesso come parametro è migliore: una versione da povero di parametri nominati che funziona sulla maggior parte delle piattaforme. E apre la porta a parametri con comportamento da avviare.

Per un esempio della maggior parte delle cose che non dovresti fare, potresti provare a leggere la documentazione della libreria standard di PHP.


0

Di solito li ordino per prima cosa, piuttosto che per una misura combinata di importanza e frequenza d'uso secondo un "sentimento" (può essere visto come ORDER BY required DESC, SOME_MAGIC_FEELING(importancy,frequency)) e non secondo una pratica specifica.

Tuttavia, come altri hanno notato, penso che il problema di fondo che lo rende un problema sta usando troppi parametri (IMHO, qualsiasi cosa> 3 è troppi) e questo è il vero problema che dovresti affrontare. C'è un post interessante al riguardo nel blog di rebecca murphey.

Penso che quando hai solo 1-3 argomenti, l'ordinamento corretto è abbastanza ovvio e "senti" ciò che è giusto.


0

Simile alla risposta di @Wyatt Barnetts, qualcosa di più di alcuni parametri o parametri molto espliciti per il metodo, raccomanderei invece di passare un oggetto. Questo è in genere più facile da aggiornare / mantenere, più chiaro da leggere ed elimina la necessità di preoccuparsi dell'ordinazione . Inoltre, troppi parametri per un metodo sono un odore di codice e ci sono schemi di refactoring comuni che puoi seguire per aiutarti a correggerlo.

Esempio esplicito:

public int add(int left, int right)
{
  return left + right;
}

Dal momento che questo è un esempio chiaramente definito e l'aggiunta è commutativa (l'ordine non ha importanza), allora basta andare con esso.

Tuttavia, se aggiungi qualcosa di più complesso:

public SomeComplexReturnValue Calculate(int i, float f, string s, object o)
{
  // do work here
}

Potrebbe diventare:

public class SomeComplexInputForCalculation
{
  public int i; 
  public float f;
  public string s; 
  public object o;
}

public SomeComplexReturnValue Calculate(SomeComplexInputForCalculation input)
{
  // do work here
}

Spero che sia di aiuto...


0

Ordinalo nel modo che ritieni possa trarre beneficio dal curry. Ad esempio, prima i parametri della funzione.


0

"Prima importante" non significa molto. Ci sono alcune convenzioni.

Se passi l'oggetto chiamante (spesso chiamato mittente), quello va per primo.

Dovrebbe esserci un po 'di fluidità nella lista, il che significa che dovresti sapere di cosa tratta un argomento quando lo leggi. Esempio:

CopyFolder (percorso stringa, bool ricorsivo);

Se dovessi mettere per primo ricorsivo, sarebbe confuso perché non c'è ancora alcun contesto. Se già adesso si tratta di copiare (1), una cartella (2), allora l'argomento ricorsivo inizia a dare un senso.

Questo fa anche funzionare bene le funzionalità simili a IntelliSense: l'utente impara mentre riempie gli argomenti, sviluppa una comprensione del metodo e di ciò che fa. Allo stesso modo, i sovraccarichi dovrebbero mantenere lo stesso ordine per le parti uguali.

Quindi potresti ancora trovare degli argomenti "uguali" a questo proposito e potresti essere tentato di lasciare che l'ordine dipenda dal tipo di argomento. Solo perché avere un paio di stringhe di fila e poi un paio di booleani sembra più bello. Ad un certo livello questo diventerà un'arte piuttosto che un'abilità.

Non mi interessa molto l'affermazione che dovresti avvolgere tutti gli argomenti in un oggetto solo per finire con un singolo argomento. Questo ti sta solo prendendo in giro (non rende il metodo meno complesso, ha ancora tutti questi argomenti, li hai appena nascosti). Questo può essere ancora conveniente da fare se si passa il set da un metodo all'altro un paio di volte, il refactoring diventerà sicuramente molto più semplice, ma dal punto di vista del design è solo fingendo di fare la differenza. Non è che finirai con un oggetto significativo che rappresenta qualcosa, sarà solo un mucchio di argomenti, non diversi dall'elenco nella dichiarazione del metodo. Non renderà felice lo zio Bob.

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.