J'ai remarqué que quelques fonctions avec lesquelles je travaille ont 6 paramètres ou plus, alors que dans la plupart des bibliothèques que j'utilise, il est rare de trouver une fonction qui en prend plus que 3.
Souvent, beaucoup de ces paramètres supplémentaires sont des options binaires pour modifier le comportement de la fonction. Je pense que certaines de ces fonctions à paramètres multiples devraient probablement être refactorisées. Existe-t-il une directive pour déterminer quel nombre est trop?
functions
parameters
Darth Egregious
la source
la source
Réponses:
Je n'ai jamais vu de guide, mais d'après mon expérience, une fonction qui prend plus de trois ou quatre paramètres indique l'un des deux problèmes suivants:
Il est difficile de dire ce que vous regardez sans plus d'informations. Il est probable que le refactoring que vous devez faire est de scinder la fonction en fonctions plus petites, appelées à partir du parent, en fonction des indicateurs actuellement transmis à la fonction.
En procédant ainsi, il y a des avantages à obtenir:
if
structure qui appelle beaucoup de méthodes avec des noms descriptifs qu'une structure qui fait tout en une seule méthode.la source
Selon "Clean Code: Un manuel d'artisanat agile", zéro est l'idéal, un ou deux sont acceptables, trois dans des cas spéciaux et quatre ou plus, jamais!
Les mots de l'auteur:
Dans ce livre, il y a un chapitre qui ne traite que des fonctions pour lesquelles les paramètres sont largement discutés. Je pense donc que ce livre peut constituer une bonne indication du nombre de paramètres dont vous avez besoin.
À mon avis, un paramètre est préférable à personne car je pense que ce qui se passe est plus clair.
Par exemple, à mon avis, le deuxième choix est préférable car la méthode utilisée est plus claire:
contre.
À propos de beaucoup de paramètres, cela peut être un signe que certaines variables peuvent être regroupées dans un seul objet ou, dans ce cas, beaucoup de booléens peuvent indiquer que la fonction / méthode en fait plus qu'une chose, et dans ce cas, est préférable de refactoriser chacun de ces comportements dans une fonction différente.
la source
String language = detectLanguage(someText);
. Dans les deux cas où vous avez passé exactement le même nombre d'arguments, il arrive que vous ayez scindé l'exécution de la fonction en deux à cause d'un langage médiocre.Matrix.Create(input);
où seinput
trouve un .NETIEnumerable<SomeAppropriateType>
? De cette façon, vous n'avez pas non plus besoin d'une surcharge séparée lorsque vous voulez créer une matrice contenant 10 éléments au lieu de 9.Si les classes de domaine de l'application sont correctement conçues, le nombre de paramètres que nous transmettons à une fonction sera automatiquement réduit, car les classes savent comment faire leur travail et disposent de suffisamment de données pour effectuer leur travail.
Par exemple, supposons que vous ayez une classe de gestionnaires qui demande à une classe de 3e année de terminer les travaux.
Si vous modélisez correctement,
C'est simple.
Si vous n'avez pas le bon modèle, la méthode sera la suivante
Le modèle correct réduit toujours les paramètres de fonction entre les appels de méthode car les fonctions correctes sont déléguées à leurs propres classes (responsabilité unique) et disposent de suffisamment de données pour effectuer leur travail.
Chaque fois que le nombre de paramètres augmente, je vérifie mon modèle pour voir si j'ai correctement conçu mon modèle d'application.
Il existe cependant quelques exceptions: lorsque je dois créer un objet de transfert ou des objets de configuration, je vais d'abord utiliser un modèle de générateur pour générer de petits objets construits avant de construire un grand objet de configuration.
la source
Un aspect que l’autre réponse ne prend pas est la performance.
Si vous programmez dans un langage de niveau suffisamment bas (C, C ++, assemblage), un grand nombre de paramètres peut être très préjudiciable aux performances de certaines architectures, notamment si la fonction est appelée un grand nombre de fois.
Quand un appel de fonction est effectué dans le bras , par exemple, les quatre premiers arguments sont placés dans les registres
r0
àr3
et autres arguments doivent être poussées sur la pile. Garder un nombre d'arguments inférieur à cinq peut faire toute une différence pour les fonctions critiques.Pour les fonctions qui sont appelées très souvent, même le fait que le programme doit mettre en place les arguments avant chaque appel peut affecter les performances (
r0
àr3
peut être remplacé par la fonction appelée et devra être remplacé avant le prochain appel) si à cet égard zéro argument est le meilleur.Mise à jour:
KjMag aborde le sujet intéressant de l'inline. Cela permet, d’une certaine manière, d’atténuer cette situation, car cela permettra au compilateur d’effectuer les mêmes optimisations que celles que vous auriez pu réaliser si vous écriviez en assemblage pur. En d'autres termes, le compilateur peut voir quels paramètres et quelles variables sont utilisés par la fonction appelée et peut optimiser l'utilisation des registres de manière à minimiser la lecture / écriture de la pile.
Il y a cependant quelques problèmes avec l'inline.
inline
était considérée comme obligatoire dans tous les cas.inline
ou vous donner des erreurs quand ils le rencontrent.la source
inline
.)Lorsque la liste de paramètres passe à plus de cinq, définissez une structure ou un objet "context".
Ceci est fondamentalement juste une structure qui contient tous les paramètres optionnels avec quelques valeurs par défaut sensibles définies.
Dans le monde procédural C, une structure simple ferait l'affaire. En Java, C ++, un objet simple suffira. Ne plaisante pas avec les accesseurs ou les setters car le seul but de l’objet est de maintenir des valeurs "publiquement" réglables.
la source
Non, il n'y a pas de directive standard
Cependant, certaines techniques peuvent rendre une fonction avec beaucoup de paramètres plus supportable.
Vous pouvez utiliser un paramètre list-if-args (args *) ou un paramètre dictionary-of-args (kwargs
**
)Par exemple, en python:
Les sorties:
Ou vous pouvez utiliser la syntaxe de définition littérale d'objet
Par exemple, voici un appel JavaScript jQuery pour lancer une demande AJAX GET:
Si vous regardez la classe ajax de jQuery, il y a beaucoup (environ 30) propriétés supplémentaires qui peuvent être définies; principalement parce que les communications ajax sont très complexes. Heureusement, la syntaxe littérale des objets facilite la vie.
C # intellisense fournit une documentation active des paramètres, il n’est donc pas rare de voir des arrangements très complexes de méthodes surchargées.
Les langages à typage dynamique comme python / javascript n’ont pas cette capacité, il est donc beaucoup plus courant de voir les arguments de mot-clé et les définitions littérales d’objet.
Je préfère les définitions littérales d'objet ( même en C # ) pour la gestion de méthodes complexes, car vous pouvez voir explicitement quelles propriétés sont définies lorsqu'un objet est instancié. Vous devrez faire un peu plus de travail pour gérer les arguments par défaut, mais à long terme, votre code sera beaucoup plus lisible. Avec les définitions littérales d'objet, vous pouvez cesser de dépendre de la documentation pour comprendre ce que fait votre code à première vue.
IMHO, les méthodes surchargées sont fortement surestimées.
Remarque: Si je me souviens bien, le contrôle d'accès en lecture seule devrait fonctionner pour les constructeurs littéraux d'objets en C #. Ils fonctionnent essentiellement de la même manière que la définition des propriétés dans le constructeur.
Si vous n'avez jamais écrit de code non trivial dans un langage Java / typé dynamiquement (python) et / ou fonctionnel / prototype, je vous suggère fortement de l'essayer. Ce peut être une expérience enrichissante.
Il peut être effrayant d’abord de rompre votre dépendance à l’égard des paramètres pour l’initialisation de la fonction / méthode, mais vous apprendrez à en faire beaucoup plus avec votre code sans ajouter de complexité inutile.
Mise à jour:
J'aurais probablement dû fournir des exemples pour illustrer l'utilisation dans un langage à typage statique, mais je ne pense pas actuellement à un contexte à typage statique. En gros, j'ai trop travaillé dans un contexte typé dynamiquement pour basculer subitement.
Ce que je ne sais est objet syntaxe de définition littérale est tout à fait possible dans les langues statiquement typés (au moins en C # et Java) parce que je les ai utilisés auparavant. Dans les langages à typage statique, ils sont appelés "initialiseurs d'objet". Voici quelques liens pour montrer leur utilisation en Java et en C # .
la source
Personnellement, plus de 2, c’est là que mon code déclenche une alarme d’odeur. Lorsque vous considérez des fonctions comme des opérations (c’est-à-dire une traduction d’entrée en sortie), il est rare que plus de 2 paramètres soient utilisés dans une opération. Les procédures (c'est-à-dire une série d'étapes pour atteindre un objectif) nécessiteront davantage d'intrants et constitueront parfois la meilleure approche, mais dans la plupart des langues, ces jours ne devraient pas être la norme.
Mais encore une fois, c'est la ligne directrice plutôt qu'une règle. J'ai souvent des fonctions qui prennent plus de deux paramètres en raison de circonstances inhabituelles ou d'une facilité d'utilisation.
la source
Comme le dit Evan Plaice, je suis un fervent partisan de la simple insertion de tableaux associatifs (ou de la structure de données comparable de votre langage) dans des fonctions autant que possible.
Ainsi, au lieu de (par exemple) ceci:
Aller pour:
Wordpress fait beaucoup de choses de cette façon, et je pense que cela fonctionne bien. (Bien que mon exemple de code ci-dessus soit imaginaire et ne soit pas un exemple de Wordpress.)
Cette technique vous permet de passer facilement beaucoup de données dans vos fonctions, tout en vous évitant d'avoir à vous rappeler de l'ordre dans lequel chacune doit être passée.
Vous apprécierez également cette technique lorsque vient le temps de refactoriser - au lieu de devoir potentiellement modifier l'ordre des arguments d'une fonction (par exemple, lorsque vous réalisez que vous devez passer Yet Another Argument), vous n'avez pas besoin de modifier le paramètre de votre fonction. liste du tout.
Non seulement cela vous évite d'avoir à réécrire la définition de votre fonction, mais vous évite également de devoir modifier l'ordre des arguments chaque fois que la fonction est appelée. C'est une victoire énorme.
la source
Une réponse précédente mentionnait un auteur fiable qui a déclaré que moins vous avez de paramètres dans vos fonctions, mieux vous vous en portez. La réponse n'explique pas pourquoi mais les livres l'expliquent. Voici deux des raisons les plus convaincantes pour lesquelles vous devez adopter cette philosophie et avec lesquelles je suis personnellement d'accord:
Les paramètres appartiennent à un niveau d'abstraction différent de celui de la fonction. Cela signifie que le lecteur de votre code devra réfléchir à la nature et au but des paramètres de vos fonctions: cette pensée est "de niveau inférieur" à celle du nom et du but des fonctions correspondantes.
La seconde raison d’avoir le moins de paramètres possibles pour une fonction est le test: par exemple, si vous avez une fonction à 10 paramètres, réfléchissez au nombre de combinaisons de paramètres nécessaires pour couvrir tous les scénarios de test, par exemple pour une unité. tester. Moins de paramètres = moins de tests.
la source
Pour donner un peu plus de contexte aux conseils relatifs au nombre idéal d'arguments de fonction nuls dans le "Code propre: manuel pour la maîtrise du logiciel agile" de Robert Martin, l'auteur cite notamment l'un des points suivants:
Pour son
includeSetupPage()
exemple ci-dessus, voici un extrait de son "code propre" refactorisé à la fin du chapitre:L '"école de pensée" de l'auteur plaide pour des classes réduites, un nombre faible (idéalement, 0) d'arguments de fonction et de très petites fonctions. Bien que je ne sois pas non plus totalement d’accord avec lui, j’ai trouvé cela stimulant et j’ai le sentiment que l’idée des arguments de fonction nulle en tant qu’idéal peut valoir la peine d’être examinée. Notez également que même son petit extrait de code ci-dessus possède des fonctions d’argument non nulles, je pense donc que cela dépend du contexte.
(Et comme d'autres l'ont fait remarquer, il affirme également que plus d'arguments compliquent les choses d'un point de vue test. Mais je souhaitais ici principalement mettre en évidence l'exemple ci-dessus et sa justification pour des arguments à fonction nulle.)
la source
Idéalement zéro. Un ou deux vont bien, trois dans certains cas.
Quatre ou plus est généralement une mauvaise pratique.
Outre les principes de responsabilité unique que d'autres ont mentionnés, vous pouvez également y penser du point de vue des tests et du débogage.
S'il y a un paramètre, connaître ses valeurs, les tester et trouver une erreur avec elles est relativement facile car il n'y a qu'un seul facteur. Au fur et à mesure que vous augmentez les facteurs, la complexité totale augmente rapidement. Pour un exemple abstrait:
Envisagez un programme «quoi porter par ce temps». Considérez ce que cela pourrait faire avec une entrée - la température. Comme vous pouvez l’imaginer, les résultats des vêtements à porter sont assez simples en fonction de ce facteur. Maintenant, réfléchissez à ce que le programme pourrait / devrait / devrait faire si la température, l’humidité, le point de rosée, les précipitations, etc. sont dépassés
la source