Les routines peuvent avoir des paramètres, ce n'est pas une nouvelle. Vous pouvez définir autant de paramètres que nécessaire, mais trop nombreux rendront votre routine difficile à comprendre et à maintenir.
Bien sûr, vous pouvez utiliser une variable structurée comme solution de contournement: en plaçant toutes ces variables dans une même structure et en les transmettant à la routine. En fait, utiliser des structures pour simplifier les listes de paramètres est l’une des techniques décrites par Steve McConnell dans Code Complete . Mais comme il dit:
Les programmeurs attentifs évitent de regrouper des données plus que ce qui est logiquement nécessaire.
Donc, si votre routine a trop de paramètres ou si vous utilisez une structure pour masquer une grande liste de paramètres, vous faites probablement quelque chose de mal. Autrement dit, vous ne gardez pas le couplage lâche.
Ma question est la suivante: quand puis-je considérer une liste de paramètres trop grande? Je pense que plus de 5 paramètres sont trop nombreux. Qu'est-ce que tu penses?
Quand une chose est-elle considérée comme si obscène qu'elle peut être réglementée malgré la garantie du 1er amendement relative à la liberté d'expression? Selon le juge Potter Stewart, "je le sais quand je le vois". La même chose est valable ici.
Je déteste faire des règles strictes comme celle-ci parce que la réponse change non seulement en fonction de la taille et de la portée de votre projet, mais je pense que cela change même au niveau du module. Selon ce que fait votre méthode ou ce que la classe est supposée représenter, il est tout à fait possible que 2 arguments soient trop nombreux et constituent le symptôme d'un couplage trop important.
Je suggérerais qu'en posant la question en premier lieu et en qualifiant votre question autant que vous le faisiez, vous savez vraiment tout cela. Dans ce cas, la meilleure solution consiste à ne pas compter sur un nombre précis et rapide, mais plutôt à rechercher des critiques de conception et de code parmi vos pairs afin d'identifier les zones où la cohésion et le couplage sont faibles.
N'ayez jamais peur de montrer votre travail à vos collègues. Si vous en avez peur, c'est probablement le signe plus important que quelque chose ne va pas dans votre code et que vous le savez déjà.
Une fonction ne peut avoir trop de paramètres si certains d'entre eux sont redondants. Si tous les paramètres sont utilisés, la fonction doit avoir le nombre correct de paramètres. Prenez cette fonction souvent utilisée:
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
);
C'est 12 paramètres (9 si vous regroupez les x, y, w et h sous forme de rectangle) et il y a aussi les paramètres dérivés du nom de la classe. Comment réduiriez-vous cela? Voudriez-vous réduire le nombre plus précisément?
Ne laissez pas le nombre de paramètres vous déranger, assurez-vous que c'est logique et bien documenté et laissez intellisense* T'aider.
* D'autres assistants de codage sont disponibles!
Dans Clean Code , Robert C. Martin a consacré quatre pages au sujet. Voici le Gist:
Le nombre idéal d'arguments pour une fonction est zéro (niladique). Vient ensuite l'un (monadique), suivi de près par deux (dyadique). Trois arguments (triadiques) doivent être évités autant que possible. Plus de trois (polyadiques) nécessitent une justification très spéciale - et ne devraient donc pas être utilisés de toute façon.
Certains codes avec lesquels j'ai travaillé dans le passé utilisaient des variables globales simplement pour éviter de transmettre trop de paramètres.
S'il vous plaît ne faites pas ça!
(Habituellement.)
Si vous commencez à compter mentalement les paramètres de la signature et à les faire correspondre à l'appel, il est temps de refactoriser!
Merci beaucoup pour toutes vos réponses:
C'était un peu surprenant de trouver des personnes qui pensent aussi (comme moi) que 5 paramètres sont une bonne limite pour la santé du code.
En général, les gens sont d’accord pour dire qu’une limite de 3 à 4 est une bonne règle empirique. C'est raisonnable, car les gens ont généralement du mal à compter plus de 4 choses.
Comme Milan points , les gens peuvent en moyenne garder plus ou moins 7 choses à la fois dans leur tête. Mais je pense que vous ne pouvez pas oublier que, lorsque vous concevez/maintenez/étudiez une routine, vous devez garder à l’esprit plus que des paramètres.
Certaines personnes considèrent qu’une routine doit avoir autant d’arguments qu’elle en a besoin. Je suis d'accord, mais seulement dans quelques cas spécifiques (appels aux API du système d'exploitation, routines pour lesquelles l'optimisation est importante, etc.). Je suggère de masquer la complexité de ces routines en ajoutant une couche d'abstraction juste au-dessus de ces appels, chaque fois que cela est possible.
Nick a quelques réflexions intéressantes à ce sujet. Si vous ne voulez pas lire ses commentaires, je résume pour vous: en un mot, cela dépend :
Je déteste faire des règles strictes comme celle-ci parce que la réponse change non seulement en fonction de la taille et de la portée de votre projet, mais je pense que cela change même au niveau du module. Selon ce que fait votre méthode ou ce que la classe est supposée représenter, il est tout à fait possible que 2 arguments soient trop nombreux et constituent le symptôme d'un couplage trop important.
La morale ici est de ne pas avoir peur de montrer votre code à vos pairs, de discuter avec eux et d'essayer de "identifier les domaines dans lesquels vous avez une faible cohésion et un couplage étroit" .
Enfin, je pense que wnoise est tout à fait d’accord avec Nick et conclut sa contribution satirique avec cette vision poétique (voir les commentaires ci-dessous) de l'art de la programmation:
La programmation n'est pas de l'ingénierie. L'organisation du code est un art car il dépend de facteurs humains, qui dépendent trop du contexte pour qu'une règle absolue puisse exister.
Cette réponse suppose un langage OO. Si vous n'en utilisez pas un - ignorez cette réponse (en d’autres termes, ce n’est pas une réponse totalement agnostique.
Si vous transmettez plus de 3 paramètres environ (en particulier les types/objets intrinsèques), ce n'est pas qu'il s'agisse d'un "Trop grand nombre" mais qu'il vous manque peut-être une chance de créer un nouvel objet.
Recherchez des groupes de paramètres qui sont passés dans plus d'une méthode - même un groupe passé dans deux méthodes garantit presque que vous devriez avoir un nouvel objet ici.
Ensuite, vous refactorisez la fonctionnalité dans votre nouvel objet et vous ne croiriez pas à quel point cela aide votre code et votre compréhension de la programmation OO.
Il semble qu'il y ait d'autres considérations que le simple nombre, voici certaines qui me viennent à l'esprit:
relation logique avec l'objectif principal de la fonction par rapport aux paramètres uniques
S'ils ne sont que des indicateurs d'environnement, le regroupement peut être très pratique
L'un des épigrammes de programmation bien connus d'Alan Perlis (relaté dans l'avis 17 (9) de l'ACM SIGPLAN, septembre 1982) indique que "Si vous avez une procédure avec 10 paramètres, vous en avez probablement oublié quelques-uns".
Selon Steve McConnell dans Code Complete, vous devriez
Limiter le nombre de paramètres d'une routine à environ sept
Pour moi, quand la liste traverse une ligne de mon IDE, alors c'est un paramètre de trop. Je veux voir tous les paramètres dans une ligne sans rompre le contact visuel. Mais ce n'est que ma préférence personnelle.
Je suis généralement d'accord avec 5, cependant, s'il y a une situation où j'ai besoin de plus et que c'est le moyen le plus clair de résoudre le problème, j'utiliserais plus.
Sept choses en mémoire à court terme?
Dans Worst 5 Code Snippets , cochez la seconde, "Est-ce un constructeur". Il a comme plus de 37 ⋅ 4 ≈ 150 paramètres:
Ici, un programmeur a écrit ce constructeur [...] Certains d'entre vous peuvent penser que c'est un gros constructeur, mais il a utilisé les outils de génération de code automatique Eclipse [.] NOO. Dans ce constructeur, il y avait un petit bug que j'ai découvert conclure que ce constructeur a été écrit à la main. (Au fait, ce n'est que la partie supérieure du constructeur, ce n'est pas complet).
Un de plus que nécessaire. Je ne veux pas être désinvolte, mais certaines fonctions nécessitent nécessairement pas mal d'options. Par exemple:
void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset);
Il y a 6 arguments, et chacun d'entre eux est essentiel. De plus, il n'y a pas de lien commun entre eux pour justifier leur regroupement. Peut-être que vous pourriez définir "struct mmapargs", mais ce serait pire.
Selon Meilleures pratiques Perl, 3 n’est pas grave, 4, c’est trop. Ce n'est qu'une ligne directrice, mais c'est ce que nous essayons de respecter dans notre magasin.
Moins et vous perdez la flexibilité.
Une question connexe que vous devriez envisager est de savoir comment cohérente la routine est. Un grand nombre de paramètres peut être une odeur qui vous dit que la routine elle-même essaie de faire trop et que sa cohésion est suspecte. Je conviens qu’un nombre difficile de paramètres est probablement impossible, mais j’imagine qu’une routine avec une cohésion élevée impliquerait un petit nombre de paramètres.
Je ferais moi-même la limite pour les fonctions publiques à 5 paramètres.
IMHO, les longues listes de paramètres ne sont acceptables que dans les fonctions d'assistance privées/locales qui ne doivent être appelées qu'à partir de quelques emplacements spécifiques du code. Dans ces cas, vous devrez peut-être transmettre beaucoup d'informations d'état, mais la lisibilité ne pose pas de problème, car vous seul (ou quelqu'un qui va gérer votre code et doit comprendre les principes fondamentaux de votre module) devez vous en soucier. appeler cette fonction.
Une chose que je voudrais souligner du point de vue des performances est que, selon la façon dont vous passez des paramètres à une méthode, le fait de passer beaucoup de paramètres par valeur ralentira le programme car chaque paramètre doit être copié puis placé dans la pile.
L'utilisation d'une seule classe pour englober tous les paramètres fonctionnerait mieux car un seul paramètre passé par référence serait élégant, plus propre et plus rapide!
Je m'arrête à trois paramètres en règle générale. Plus tard, il est temps de passer à la place un tableau de paramètres ou un objet de configuration, ce qui permet également d'ajouter de nouveaux paramètres sans modifier l'API.
Une restriction de longueur sur une liste de paramètres n'est qu'une restriction de plus. Et restriction signifie violence appliquée. Cela semble drôle, mais vous pouvez être non violent même lorsque vous programmez. Laissez le code dicter les règles. Il est évident que si vous avez plusieurs paramètres, le corps de la méthode function/class sera suffisamment grand pour pouvoir les utiliser. Et les gros extraits de code peuvent généralement être refactorisés et divisés en fragments plus petits. Vous avez donc la solution de ne pas avoir beaucoup de paramètres en tant que bonus gratuit, car ils sont partagés entre les plus petits morceaux de code refactorisés.
Selon moi, il pourrait y avoir des cas où vous dépasserez 4 ou un nombre fixe. Choses à surveiller pourrait être
Sous l’angle de la facilité d’utilisation ou de la lecture du code, je pense que lorsque vous avez besoin de "reformer les mots" de votre signature de méthode, vous devriez vous arrêter et réfléchir, à moins que vous ne vous sentiez impuissant (e) et que tous les efforts pour réduire la signature ne conduisent à. pas de résultat. Certaines très bonnes bibliothèques du passé et du présent utilisent plus de 4 à 5 poussettes.
Ma règle générale est que je dois pouvoir me souvenir des paramètres suffisamment longtemps pour pouvoir regarder un appel et en expliquer le fonctionnement. Donc, si je ne peux pas regarder la méthode puis passer à un appel d'une méthode et me rappeler quel paramètre fait quoi, alors il y en a trop.
Pour moi, cela équivaut à environ 5, mais je ne suis pas si brillant. Votre kilométrage peut varier.
Vous pouvez créer un objet avec des propriétés pour conserver les paramètres et le transmettre si vous dépassez la limite que vous avez définie. Voir le livre Refactoring de Martin Fowler et le chapitre sur la simplification des appels de méthode.
Je suis d'accord avec la citation de Robert Martin dans Clean Code ( comme cité ci-dessus ): moins il y a de paramètres, mieux c'est. Plus de 5 à 7 paramètres et appels de méthodes deviennent difficiles à comprendre. Les choses deviennent particulièrement pénibles si certains paramètres sont facultatifs (et prennent donc des valeurs nulles) ou si tous les paramètres ont le même type (ce qui rend encore plus difficile de déterminer quel paramètre correspond à quel paramètre). Si vous pouvez regrouper des paramètres dans des objets de domaine cohérents tels que Client et Compte, votre code sera beaucoup plus agréable à utiliser.
Il y a un cas Edge: si vous avez un appel de méthode qui prend un nombre variable de paramètres qui forment un ensemble logique , il y a moins de surcharge cognitive avec plus de paramètres . Par exemple, vous aurez peut-être besoin d'une méthode qui spécifie un nombre de tentatives de requête HTTP, en termes de nombre de millisecondes entre les tentatives. Trois tentatives à intervalles de 1, 2 et 3 pourraient être spécifiées comme suit:
retries(1000, 2000, 3000)
Dans ce cas limité, ajouter plus de paramètres à un appel n'augmente pas autant la surcharge mentale.
Une autre considération est de savoir si votre langue supporte les listes de paramètres nommés et vous permet de laisser de côté les paramètres facultatifs. Les grandes listes de paramètres nommés sont plus faciles à comprendre que les grandes listes de paramètres non nommées.
Mais je préférerais toujours moins de paramètres que de plus.
Si j'ai 7 à 10 paramètres dans une routine, je cherche à les regrouper dans une nouvelle classe mais pas si cette classe ne serait qu'un tas de champs avec des getters et des setters - la nouvelle classe doit fait autre chose que mélanger les valeurs. Sinon, je préférerais utiliser la longue liste de paramètres.
Je baserais ma réponse sur la fréquence à laquelle la fonction est appelée.
S'il s'agit d'une fonction init qui n'est appelée qu'une fois, laissez-la prendre 10 pars ou plus, qui s'en soucie.
Si on l'appelle plusieurs fois par image, j'ai tendance à créer une structure et à lui passer un pointeur, car cela a tendance à être plus rapide (en supposant que vous ne reconstruisez pas la structure à chaque fois).
Cela dépend fortement de l'environnement dans lequel vous travaillez. Prenez par exemple le javascript. En javascript, le meilleur moyen de passer des paramètres consiste à utiliser des objets avec des paires clé/valeur, ce qui signifie en pratique que vous n'avez qu'un seul paramètre. Dans d'autres systèmes, le point idéal sera à trois ou quatre.
En fin de compte, tout se résume à un goût personnel.
Je suis d'accord avec 3 c'est correct, 4 est trop comme une ligne directrice. Avec plus de 3 paramètres, vous effectuez inévitablement plus d'une tâche. Plus d'une tâche doit être divisée en méthodes séparées.
Toutefois, si j’examinais le dernier projet sur lequel j’ai travaillé, les exceptions seraient légion et il serait difficile d’abaisser la plupart des cas à trois paramètres.
Selon Jeff Bezos de la célébrité amazonienne, pas plus que ce qui peut être nourri avec deux pizzas :
C'est un fait connu que, en moyenne, les gens peuvent garder 7 +/- 2 choses dans leur tête à la fois. J'aime utiliser ce principe avec des paramètres. En supposant que les programmeurs soient tous des personnes intelligentes au-dessus de la moyenne, je dirais que tout ce qui est supérieur à 10, c'est trop.
BTW, si les paramètres sont similaires de quelque façon que ce soit, je les mettrais dans un vecteur ou une liste plutôt que dans une structure ou une classe.
OMI, la justification d'une longue liste de paramètres est que les données ou le contexte sont de nature dynamique, pensez à printf (); un bon exemple d'utilisation de varargs. Un meilleur moyen de gérer de tels cas consiste à passer un flux ou une structure XML, ce qui minimise à nouveau le nombre de paramètres.
Un grand nombre d'arguments ne dérangerait sûrement pas une machine, mais les développeurs pensent également aux coûts de maintenance, au nombre de tests élémentaires et aux contrôles de validation. Les concepteurs détestent également la longue liste d'arguments. Plus d'arguments signifient plus de modifications des définitions d'interface, chaque fois qu'une modification doit être apportée. Les questions sur le couplage/la cohésion découlent des aspects ci-dessus.
Je dirais que tant que vous avez des surcharges qui ont 2-4 que vous êtes bon pour monter plus haut si vous en avez besoin.
Je pense que le nombre réel dépend vraiment de ce qui a du sens logique avec le contexte de la fonction. Je conviens qu’environ 4 ou 5 paramètres commencent à être encombrés.
Dans le cas de la définition d'indicateurs, un bon moyen de gérer cette situation consiste à énumérer les valeurs et OR ensemble.