web-dev-qa-db-fra.com

Quelle est la meilleure pratique pour classer les paramètres d'une fonction?

Parfois (rarement), il semble que la création d'une fonction qui prenne une quantité décente de paramètres soit la meilleure voie. 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 méthode de "meilleures pratiques" pour classer les paramètres qui améliore la clarté?

55
Casey Patton

En général: tilisez-le.

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

Et voyez dans quel ordre vous les avez déposés.

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

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

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

55
ZJR

J'utilise généralement ces règles, mais pas toujours avec la même priorité. Je suppose que c'est un processus de réflexion automatique maintenant, et je ne le pense pas trop, sauf pour la conception d'API publique .

Entonnoir de sélection

  1. Sémantique
  2. Importance/pertinence
  3. Fréquence d'utilisation
  4. Préoccupations d'E/S

1. La sémantique d'abord

En particulier dans la POO, choisissez des 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:

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

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

2. Puis l'importance

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

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 sur 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 Nième paramètre (sauf si votre langue a un concept de paramètre d'espace réservé ).

La bonne nouvelle pour vous est qu'en général, la fréquence est liée à l'importance, ce qui va de pair avec le point précédent. Et puis c'est probablement à vous de créer 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 produit une sortie, et que cette dernière ne doit pas être "renvoyée" (via une instruction return) ou "levée" (en utilisant un système d'exception), alors vous avez la possibilité de passer valeurs à l'appelant en utilisant vos autres paramètres (ou le paramètre d'entrée). Cela concerne la sémantique, et dans la plupart des cas, il sera logique que les premiers paramètres définissent la sortie et que les derniers paramètres reçoivent la sortie.

De plus, une autre approche pour avoir moins de paramètres et maximiser la sémantique serait d'utiliser une approche fonctionnelle ou de définir un modèle Builder , afin que vous puissiez clairement empiler vos entrées, définir vos sorties et les récupérer en cas de besoin.

(Remarquez que je ne mentionne pas de variables globales, car pourquoi en utiliseriez-vous une, non?)


Quelques points à considérer

  • Facilité d'utilisation

    La plupart des éléments ci-dessus s'afficheront naturellement si vous suivez ZJR conseil : Utilisez-le!

  • Envisagez une refactorisation

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

  • Tenez compte des performances

    N'oubliez pas que les implémentations de certains langages auront des impacts très importants sur la gestion de la mémoire d'exécution lors de l'utilisation des paramètres. D'où la raison pour laquelle les livres de style de nombreuses langues recommandent de garder la liste des paramètres simple et courte . À 4 paramètres max, par exemple. Je vous laisse un exercice pour comprendre pourquoi.

  • Les recommandations de Bevan réponse et la mention de Code propre sont tout aussi pertinentes!

28
haylem

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

Dans le livre de l'oncle Bob " Clean Code ", il préconise, de façon 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 soit évidente, soit sans importance.

Cependant, imparfaitement, j'essaie de suivre les conseils de l'oncle Bob - et cela améliore mon code.

Dans les rares cas où une méthode semble exiger plus d'informations, l'introduction d'un objet paramètre est une bonne idée. Habituellement, 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.

20
Bevan

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

10
quant_dev

Je n'ai jamais vu de "meilleure pratique" documentée concernant ce sujet particulier, mais ma norme personnelle est de les répertorier 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 pass-through à une couche de données, je vais les répertorier dans l'ordre dans lequel elles apparaissent dans le schéma db ou les méthodes de la couche de données.

De plus, lorsqu'il y a plusieurs surcharges d'une méthode, je remarque que la manière typique consiste à les répertorier en commençant par des paramètres communs à toutes (ou la plupart) des méthodes, chaque méthode étant ajoutée à la fin pour chaque surcharge de méthode, 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) { }
6
Joel Etherton

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

6
J.K.

Je suis souvent la convention C/C++ de placer d'abord les paramètres const (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 nécessairement la bonne méthode d'appel des fonctions mais, si vous êtes intéressé par la façon dont chaque compilateur gère les paramètres, consultez les liens suivants pour les règles régissant et/ou l'ordre dans lequel les paramètres sont poussés dans la pile.

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

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

3
Bill

Parfois (rarement), il semble que la création d'une fonction qui prenne une quantité décente de paramètres soit la meilleure voie.

L'utilisation de plusieurs paramètres est souvent un indicateur clair, que vous violez le SRP dans cette méthode. Il est peu probable qu'une méthode, qui nécessite de nombreux paramètres, ne fasse qu'une seule chose . Excpetion peut être une fonction mathématique ou une méthode de configuration, où en effet plusieurs paramètres en tant que tels sont nécessaires. J'éviterais plusieurs paramètres car le diable évite l'eau bénite. Plus vous utilisez de paramètres dans une méthode, plus vous avez de chances que la méthode soit (trop) complexe; plus la complexité signifie: plus difficile à entretenir et moins souhaitable.

Cependant, quand je le fais, j'ai l'impression que je choisis 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 [~ # ~] b [~ # ~] est le paramètre le plus pertinent. Donc, même si vous étiez attentif dans le choix de la commande - pour d'autres, cela pourrait sembler aléatoire .

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

Il existe plusieurs solutions:

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

b) Comme vous ne l'avez pas précisé, quelle langue vous avez choisie, il est possible que vous ayez choisi une langue avec paramètres nommés . C'est du sucre syntaxique Nice 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. Et alors?

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

d) Bien sûr, si vous travaillez avec un langage typé statiquement comme Java. vous pourriez utiliser un Hashmap , mais vous perdriez les informations de type (par exemple lorsque vous travaillez avec HashMap<String, Object>) lorsque les paramètres ont différents types (et doivent être casté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 un struct (si vous écrivez par exemple C # ou C/C++).

Règle générale:

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

2) Bon cas - votre méthode a besoin d'un paramètre

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

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

1
Thomas Junk

En général, je vais simplement avec l'ordre des paramètres "ce qui semble moins cyprtic". Moins j'ai besoin d'aller à la définition de la méthode/fonction, mieux c'est. Et c'est bien d'avoir nommé des paramètres qui sont descriptifs quant à leur utilisation, 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 voudrez peut-être envisager une conception différente. Prenez du recul et voyez comment vous pouvez diviser cela en plusieurs 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.

1
user29981

Souvent, un objet complexe en tant que paramètre est meilleur - la version d'un pauvre homme de paramètres nommés qui fonctionne sur la plupart des plates-formes. Et ouvre la porte aux paramètres avec un comportement pour démarrer.

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

0
Wyatt Barnett

"Important d'abord" ne signifie pas grand-chose. Il y a quelques conventions.

Si vous passez l'objet appelant (souvent nommé expéditeur), cela passe en premier.

Il devrait y avoir fluency dans la liste, ce qui signifie que vous devriez savoir de quoi parle un argument au moment où vous le lisez. Exemple:

CopyFolder (chemin de 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 déjà, il s'agit de 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 en remplissant 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 pièces égales.

Ensuite, vous pouvez toujours trouver qu'il existe des arguments qui sont "égaux" à cet égard et vous pouvez être tenté de laisser l'ordre dépendre du type d'argument. Tout simplement parce qu'avoir quelques cordes d'affilée, puis quelques booléens, c'est plus joli. À un certain niveau, cela deviendra un art plutôt qu'une compétence.

Peu m'importe la déclaration selon laquelle vous devez envelopper tous les arguments dans un objet juste pour vous retrouver avec un seul argument. Cela ne fait que vous tromper (cela ne rend pas la méthode moins complexe, elle a toujours tous ces arguments, vous les cachez juste). Cela peut toujours être pratique si vous passez l'ensemble de méthode en méthode plusieurs fois, le refactoring deviendra certainement beaucoup plus facile, mais en termes de conception, il s'agit simplement de faire semblant de faire la différence. Ce n'est pas comme si vous vous retrouviez 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 méthode. Cela ne rendra pas l'oncle Bob heureux.

0
Martin Maat

Je les commande généralement avec les premiers requis, puis par une mesure combinée d'importance et de 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é, je pense que le problème sous-jacent qui en fait un problème utilise trop de paramètres (à mon humble avis, tout ce qui est> 3 est trop) et c'est le vrai 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, le bon ordre est assez évident et vous "sentez" juste ce qui est juste.

0
shesek

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 recommanderais plutôt de passer un objet. Ceci est généralement plus facile à mettre à jour/maintenir, plus clair à lire et supprime la nécessité de se soucier de la commande . En outre, trop de paramètres pour une méthode est un odeur de code et il existe des refactoring patterns communs que vous pouvez suivre pour aider à la corriger.

Exemple explicite:

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

Comme il s'agit d'un exemple assez clairement défini et que l'addition est commutative (l'ordre n'a pas d'importance), alors allez-y.

Cependant, 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...

0
longda

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

0
alternative