Quelle est la meilleure pratique pour la commande de paramètres dans une fonction?

52

Parfois (rarement), il semble que la meilleure voie consiste à créer une fonction qui prend une quantité décente de paramètres. Cependant, quand je le fais, j'ai l'impression de choisir souvent l'ordre des paramètres au hasard. Je vais généralement par "ordre d'importance", avec le paramètre le plus important en premier.

Y a-t-il une meilleure manière de faire cela? Existe-t-il une "bonne pratique" pour classer les paramètres qui améliore la clarté?

Casey Patton
la source
5
Les paramètres nommés rendent cela moins qu'un problème. Python le prend à l'extrême, examinez-le. Un bon exemple est en C # - les nombreuses façons d’appeler MessageBox.Show. Regardez dans cela aussi.
Job
8
En Haskell, l'utilité possible de l'application de fonction partielle suggère généralement un ordre naturel des arguments.
tdammers
Que considéreriez-vous comme une quantité décente de paramètres?
Chris
Je pensais> 2. Bien que je suppose que la commande s'applique à 2 paramètres tout de même.
Casey Patton
3
@tdammers Ceci est également vrai de toutes les langues ayant un support direct pour currying
jk.

Réponses:

55

En général: utilisez-le .

Ecrivez un test pour votre fonction, un test du monde réel.
Quelque chose que vous aimeriez réellement faire avec cette fonction .

Et voyez dans quel ordre vous les avez mis.

Sauf si vous avez déjà (ou connaissez) certaines fonctions qui font quelque chose de similaire.
Dans ce cas: conformez-vous à ce qu'ils font déjà, du moins pour les premiers arguments.

Par exemple, prennent-ils tous un document / objet / pointeur de fichier / série de valeurs / coordonnées comme premier (s) argument (s)? Pour l'amour de Dieu, conformez-vous à ces arguments .

Évitez de confondre vos collègues et votre futur moi .

ZJR
la source
12
"Évitez de confondre ... votre futur moi" sonne comme une bonne philosophie en général.
Jeanne Pindar
28

Je vais généralement avec ces règles, mais pas toujours avec la même priorité. Je suppose que c’est un processus de réflexion automatique à présent, et je n’y réfléchis pas trop, sauf pour la conception d’API publiques .

Entonnoir de sélection

  1. Sémantique
  2. Importance / Pertinence
  3. Fréquence d'utilisation
  4. Problèmes d'E / S

1. La sémantique d'abord

Surtout en POO, choisissez les paramètres en fonction de leur signification sémantique pour l'action ou le message. La signature d'une méthode bien nommée avec un paramètre bien nommé doit:

  • se sentir naturel d'appeler,
  • être auto-descriptif en termes d'intention et de comportement.

(Pour ces raisons, utiliser parfois des types personnalisés ou des alias au lieu de primitives peut augmenter l'expressivité de votre signature.)

2. Alors l'importance

Le paramètre le plus "significatif" vient en premier (ou suivant ...)

3. Puis la fréquence

La fréquence est également importante, en particulier dans une langue où vous n'avez pas de paramètres nommés mais pouvez avoir des valeurs par défaut pour les paramètres de position. Cela implique que l'ordre des paramètres ne varie pas et que vous ne pouvez évidemment pas définir les paramètres N + 1 si vous souhaitez forcer la valeur par défaut du paramètre Nth (sauf si votre langue utilise le concept de paramètre d'espace réservé ).

La bonne nouvelle pour vous est que généralement, la fréquence est liée à l’importance, ce qui va donc de pair avec le point précédent. Et puis, c’est probablement à vous de concevoir votre API pour qu’elle ait la sémantique appropriée.

4. N'oublions pas les E / S

si votre méthode / fonction prend une entrée et génère une sortie, et que cette dernière ne doit pas être "retournée" (via une instruction return) ou "jetée" (en utilisant un système d'exception), il vous reste l'option de passer renvoyer les valeurs à l'appelant à l'aide de vos autres paramètres (ou du paramètre d'entrée). Cela concerne la sémantique, et dans la plupart des cas, il sera judicieux que les premiers paramètres définissent la sortie, et les derniers paramètres reçoivent la sortie.

En outre, une autre approche permettant de réduire le nombre de paramètres et d'optimiser la sémantique consiste à utiliser une approche fonctionnelle ou à définir un motif Builder , afin de pouvoir clairement empiler vos entrées, définir vos sorties et les récupérer en cas de besoin.

(Remarquez que je ne mentionne pas les variables globales, car pourquoi en utiliseriez-vous une, n'est-ce pas?)


Quelques points à considérer

  • Utilisabilité

    Si vous suivez les conseils de ZJR, la plupart des éléments ci-dessus apparaîtront naturellement : utilisez-le!

  • Envisager de refactoriser

    Si vous vous inquiétez de l'ordre des paramètres, peut-être que cette inquiétude trouve sa racine dans ce qui précède et dans le fait que votre API est mal conçue. Si vous avez trop de paramètres, il est très probable que quelque chose soit composé / modularisé et refactorisé .

  • Considérer la performance

    Gardez à l'esprit que l'implémentation de certaines langues aura des conséquences très importantes sur la gestion de la mémoire d'exécution lors de l'utilisation de paramètres. D'où la raison pour laquelle les manuels de style de nombreuses langues recommandent de garder la liste des paramètres simple et courte . À 4 paramètres maximum, par exemple. Je vous laisse comme exercice pour comprendre pourquoi.

  • La réponse de Bevan et la mention des recommandations de Clean Code sont également pertinentes!

haylem
la source
18

Je soumets respectueusement que s'inquiéter de l'ordre des paramètres, c'est se soucier de la mauvaise chose.

Dans son livre " Clean Code ", il préconise de manière convaincante que les méthodes ne devraient jamais avoir plus de deux arguments - et la plupart ne devraient en avoir qu'un, le cas échéant. Dans ce cas, la commande est évidente ou sans importance.

Bien qu'imparfaitement, j'essaie de suivre le conseil de Oncle Bob - et cela améliore mon code.

Dans les rares cas où une méthode semble nécessiter plus d'informations, l'introduction d'un objet paramètre est une bonne idée. En général, je trouve que c'est la première étape vers la découverte d'un nouveau concept (objet) qui est la clé de mon algorithme.

Bevan
la source
Je suis certainement d'accord avec vous! Ma première déclaration visait à faire comprendre que ce n’est pas une chose typique pour moi, haha. Mais dans le cas où je trouve que j'ai vraiment besoin de passer certains paramètres et que ce n'est pas la peine de refactoriser tout le programme, je me pose des questions sur la commande.
Casey Patton
3
toux PHP toux . Je suis désolé - vous disiez quelque chose sur le fait de ne pas vous soucier de l'ordre des paramètres?
Craige
N'auriez-vous pas alors juste un constructeur pour votre objet paramètre qui prend autant d'arguments?
Détly
Non - parce que l'objet paramètre peut être "construit" sur plusieurs instructions de manière plus lisible.
Bevan
2
@Bevan: c'est très discutable. Construire des objets de cette façon signifie qu'ils ne peuvent plus être immuables; Garder vos objets immuables chaque fois que possible est cependant un autre excellent moyen d'augmenter la maintenabilité.
tdammers
10

J'essaie de mettre les paramètres IN en premier, les paramètres OUT en second. Il existe également un ordre naturel, par exemple createPoint(double x, double y)fortement préférable à createPoint(double y, double x).

quant_dev
la source
5
Parfois, le contraire est préférable, par exemple, quand il y a toujours un seul argument OUT et un nombre variable d'arguments in, comme dans addAllTo(target, something1, something2).
Maaartinus
Bien que je suive la même règle, j'essaie d'éviter autant que possible les paramètres OUT et la plupart des bons programmeurs. Le nombre de fonctions pour lesquelles cette recommandation aide réellement le PO devrait donc être plutôt faible.
Doc Brown
7
@ DocBrown Vous ne devriez vraiment pas éviter les bons programmeurs, ils peuvent être utiles.
RyanfaeScotland
@RyanfaeScotland: zut, je devrais lire mes commentaires deux fois avant de les poster (ou au moins dans les cinq minutes, je peux les changer) ;-)
Doc Brown
6

Je n'ai jamais vu de "meilleure pratique" documentée concernant ce sujet particulier, mais ma norme personnelle est de les énumérer dans l'ordre dans lequel elles apparaîtront dans la méthode pour laquelle elles sont utilisées ou si la méthode est plus complexe. passer au travers d’une couche de données Je les énumérerai dans l’ordre dans lequel elles apparaîtront dans les méthodes de schéma de base de données ou de couche de données.

De plus, quand il y a plusieurs surcharges d'une méthode, je remarque que la manière typique est de les lister en commençant par des paramètres communs à toutes les méthodes (ou à la plupart), chaque méthode étant ajoutée à la fin de chaque surcharge, comme :

void func1(string param) { }
void func2(string param, int param2) { }
void func3(string param, string param3) { }
void func3(string param, int param2, string param3) { }
Joel Etherton
la source
6

Ordre: entrée (s), sortie (s), paramètres optionnels.

Jonathan Khoo
la source
3

Je suis souvent la convention C / C ++ constqui consiste à placer les paramètres en premier (c'est-à-dire les paramètres que vous transmettez par valeur), puis ceux que vous transmettez par référence. Ce n'est peut-être pas forcément la méthode correcte pour appeler des fonctions, mais si la manière dont chaque compilateur traite les paramètres vous intéresse, consultez les liens suivants pour connaître les règles régissant et / ou l'ordre dans lequel les paramètres sont placés dans la pile.

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

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

Facture
la source
Msdn.microsoft.com/en-us/library/984x0h58%28v=vs.71%29.aspx est un autre lien qui peut vous intéresser . Pour les fonctions récursives, consultez le lien suivant. publications.gbdirect.co.uk/c_book/chapter4/… Pas que j'ai oublié, mais étant un nouvel utilisateur, je suis incapable de poster un commentaire qui a plus de deux liens .... quelle frustration!
Bill
1

Je vais généralement juste avec l'ordre des paramètres "ce qui semble moins cyprique". Moins il me faudra aller à la définition de méthode / fonction, mieux ce sera. Et c’est agréable d’avoir des paramètres nommés qui décrivent ce à quoi ils sont utilisés, de cette façon lorsque la petite info-bulle apparaît (VS), cela le rend encore plus facile.

Si vous avez des lignes et des lignes de paramètres, vous pouvez envisager une conception différente. Prenez du recul et voyez comment vous pouvez le diviser en davantage de fonctions / méthodes. Juste une idée, mais quand j'ai une douzaine de paramètres dans ma fonction, ce n'est presque toujours pas un problème de paramètre, mais un problème de conception.


la source
2
"cyprtic": un excellent moyen de faire valoir son point de vue.
Bevan
2
Vous n'avez jamais eu de faute de frappe auparavant, @Bevan?
1
J'ai des fautes de frappe tout le temps - écrire cyprtic était tellement bon que je pensais que c'était fait exprès . S'il vous plaît changez-le en retour, il est utile de faire valoir votre point même si c'était un accident.
Bevan
1
@Bevan, haha ​​d'accord, je vois ce que vous dites. Bon point! Son changé en arrière =)
1

Parfois (rarement), il semble que la meilleure voie consiste à créer une fonction qui prend une quantité décente de paramètres.

L'utilisation de plusieurs paramètres est souvent un indicateur clair que vous violez le SRP avec cette méthode. Une méthode nécessitant de nombreux paramètres a peu de chance de faire une seule chose. L'excétion peut être une fonction mathématique ou une méthode de configuration, où plusieurs paramètres en tant que tels sont nécessaires. J'éviterais plusieurs paramètres comme le diable évite l'eau bénite. Plus vous utilisez de paramètres dans une méthode, plus il y a de chances que la méthode soit (trop) complexe; plus la complexité signifie: plus difficile à maintenir et moins souhaitable.

Cependant, quand je le fais, j'ai l'impression de choisir souvent l'ordre des paramètres au hasard. Je vais généralement par "ordre d'importance", avec le paramètre le plus important en premier.

En principe, vous choisissez au hasard . Bien sûr, vous pourriez penser que le paramètre A est plus pertinent que le paramètre B ; mais cela pourrait ne pas être le cas pour les utilisateurs de votre API, qui pensent que B est le paramètre le plus pertinent. Donc, même si vous avez été attentif dans le choix de la commande, cela pourrait sembler aléatoire pour d'autres .

Y a-t-il une meilleure manière de faire cela? Existe-t-il une "bonne pratique" pour classer les paramètres qui améliore la clarté?

Il y a plusieurs issues:

a) Le cas trivial: N'utilisez pas plus d'un paramètre.

b) Comme vous n'avez pas précisé la langue que vous avez choisie, il est possible que vous choisissiez une langue avec des paramètres nommés . C'est un bon sucre syntaxique qui vous permet de desserrer la signification de l'ordre des paramètres:fn(name:"John Doe", age:36)

Toutes les langues ne permettent pas de telles subtilités. Alors quoi alors?

c) Vous pouvez utiliser un dictionnaire / tableau de hachage / tableau associatif en tant que paramètre: par exemple, Javascript autoriserait ce qui suit: fn({"name":"John Doe", age:36})ce qui n'est pas loin de (b).

d) Bien sûr, si vous travaillez avec un langage typé statiquement , tel que Java. vous pouvez utiliser une table de hachage , mais vous perdriez des informations de type (par exemple lorsque vous travaillez avec HashMap<String, Object>) lorsque les paramètres ont des types différents (et doivent être transtypés).

La prochaine étape logique serait de passer un Object(si vous utilisez Java) avec les propriétés appropriées ou quelque chose de plus léger comme une structure (si vous écrivez par exemple C # ou C / C ++).

Règle de base:

1) Meilleur cas - votre méthode n'a besoin d' aucun paramètre

2) Bon cas - votre méthode nécessite un paramètre

3) Cas tolérable - votre méthode nécessite deux paramètres

4) Tous les autres cas devraient être refactorisés

Thomas Junk
la source
0

Souvent, un objet complexe en tant que paramètre est préférable: la version des paramètres nommés du pauvre qui fonctionne sur la plupart des plateformes. Et ouvre la porte aux paramètres avec comportement à démarrer.

Pour obtenir un exemple de la plupart des choses que vous ne devriez pas faire, essayez de lire la documentation de la bibliothèque standard de PHP.

Wyatt Barnett
la source
0

Je les commande habituellement avec le choix d’abord, puis selon une mesure combinée de l’importance et de la fréquence d’utilisation en fonction d’un "sentiment" (peut être considéré comme ORDER BY required DESC, SOME_MAGIC_FEELING(importancy,frequency)) et non selon une pratique spécifique.

Cependant, comme d’autres l’ont noté, j’estime que le problème sous-jacent qui fait que ce problème existe utilise trop de paramètres (IMHO, tout ce qui est> 3 est trop) et c’est le véritable problème que vous devriez résoudre. Il y a un article intéressant à ce sujet sur le blog de rebecca murphey.

Je pense que lorsque vous n'avez que 1 à 3 arguments, l'ordre correct est assez évident et vous ne faites que "sentir" ce qui est juste.

Shesek
la source
0

Semblable à la réponse de @Wyatt Barnetts, rien de plus que quelques paramètres ou des paramètres très explicites pour la méthode, je vous recommanderais plutôt de passer un objet. Ceci est généralement plus facile à mettre à jour / maintenir, plus clair à lire et élimine la nécessité de se soucier de la commande . En outre, trop de paramètres pour une méthode constituent une odeur de code et il existe des modèles de refactoring courants que vous pouvez suivre pour vous aider à les corriger.

Exemple explicite:

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

Puisqu'il s'agit d'un exemple assez clairement défini et que l'addition est commutative (l'ordre n'a pas d'importance), continuez simplement avec.

Toutefois, si vous ajoutez quelque chose de plus complexe:

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

Deviendrait:

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

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

J'espère que cela t'aides...

Longda
la source
0

Commandez-le de la manière dont vous pensez que le curry bénéficiera probablement. Par exemple, les paramètres de fonction en premier.

alternative
la source
0

"Important d'abord" ne veut pas dire grand chose. Il y a des conventions.

Si vous transmettez l'objet appelant (souvent appelé expéditeur), cela commence en premier.

La liste devrait être fluide , ce qui signifie que vous devez savoir ce qu’est un argument au moment où vous le lisez. Exemple:

CopyFolder (chemin de la chaîne, bool récursif);

Si vous deviez mettre récursif en premier, ce serait déroutant car il n'y a pas encore de contexte. Si vous voulez déjà copier (1), un dossier (2), alors l'argument récursif commence à avoir un sens.

Cela permet également aux fonctionnalités de type IntelliSense de bien fonctionner: l'utilisateur apprend au fur et à mesure qu'il complète les arguments, il construit une compréhension de la méthode et de ce qu'elle fait. De même, les surcharges doivent conserver le même ordre pour les parties égales.

Ensuite, vous pouvez toujours trouver des arguments "égaux" à cet égard et vous pourriez être tenté de laisser l'ordre dépendre du type de l'argument. Juste parce que quelques cordes dans une rangée puis deux booléens sont plus jolies. À un certain niveau, cela deviendra un art plutôt qu'une compétence.

Je me moque bien de l’affirmation selon laquelle vous devriez envelopper tous les arguments dans un objet pour n’avoir qu’un seul argument. Cela ne fait que vous leurrer (cela ne rend pas la méthode moins complexe, elle a encore tous ces arguments, vous les avez cachés). Cela peut quand même être pratique si vous passez plusieurs fois d'une série à l'autre, le refactoring deviendra certainement beaucoup plus facile, mais au niveau de la conception, il ne fait que prétendre que vous faites une différence. Ce n'est pas comme si vous allez vous retrouver avec un objet significatif qui représente quelque chose, ce sera juste un tas d'arguments, pas différent de la liste dans la déclaration de la méthode. Cela ne rendra pas l'oncle Bob heureux.

Martin Maat
la source