J'ai lu les premiers chapitres de Clean Code de Robert C. Martin, et il me semble que c'est assez bon, mais j'ai un doute, dans une partie il est mentionné qu'il est bon (cognitivement) que les fonctions aient aussi peu de paramètres Autant que possible, cela suggère même que 3 paramètres ou plus, c'est trop pour une fonction (que je trouve très exagérée et idéaliste), alors j'ai commencé à me poser des questions ...
Les pratiques consistant à utiliser des variables globales et à transmettre de nombreux arguments aux fonctions seraient de mauvaises pratiques de programmation, mais l'utilisation de variables globales peut considérablement réduire le nombre de paramètres dans les fonctions ...
Je voulais donc savoir ce que vous en pensez. Est-il utile d'utiliser des variables globales pour réduire le nombre de paramètres des fonctions ou pas? Dans quels cas serait-ce?
Ce que je pense, c'est que cela dépend de plusieurs facteurs:
- Taille du code source.
- Nombre de paramètres en moyenne des fonctions.
- Nombre de fonctions.
- Fréquence à laquelle les mêmes variables sont utilisées.
À mon avis, si la taille du code source est relativement petite (moins de 600 lignes de code), il existe de nombreuses fonctions, les mêmes variables sont transmises en tant que paramètres et les fonctions ont de nombreux paramètres. L'utilisation de variables globales vaudrait la peine aimerait savoir...
- Partagez-vous mon opinion?
- Que pensez-vous des autres cas où le code source est plus gros, etc.?
PS . J'ai vu ce billet , les titres sont très similaires, mais il ne me demande pas ce que je veux savoir.
postLetter(string country, string town, string postcode, string streetAddress, int appartmentNumber, string careOf)
est une version malodorante depostLetter(Address address)
. Continuez à lire le livre, j'espère qu'il dira quelque chose comme ça.Réponses:
Je ne partage pas votre opinion. À mon avis, l’utilisation de variables globales est une pratique pire que davantage de paramètres, quelles que soient les qualités que vous avez décrites. Mon raisonnement est que plus de paramètres peuvent rendre une méthode plus difficile à comprendre, mais les variables globales peuvent causer de nombreux problèmes pour le code, notamment une testabilité médiocre, des bogues de simultanéité et un couplage étroit. Quel que soit le nombre de paramètres d’une fonction, elle n’aura pas les mêmes problèmes que les variables globales.
Ce peut être une odeur de design. Si vous transmettez les mêmes paramètres à la plupart des fonctions de votre système, vous devrez peut-être résoudre un problème transversal en introduisant un nouveau composant. Je ne pense pas que transmettre la même variable à plusieurs fonctions soit un bon raisonnement pour introduire des variables globales.
Lors d'une édition de votre question, vous avez indiqué que l'introduction de variables globales pourrait améliorer la lisibilité du code. Je ne suis pas d'accord. L'utilisation de variables globales est masquée dans le code d'implémentation, tandis que les paramètres de fonction sont déclarés dans la signature. Les fonctions devraient idéalement être pures. Ils ne devraient opérer que sur leurs paramètres et ne devraient avoir aucun effet secondaire. Si vous avez une fonction pure, vous pouvez raisonner à propos de cette fonction en regardant une seule fonction. Si votre fonction n'est pas pure, vous devez tenir compte de l'état des autres composants et il devient beaucoup plus difficile de raisonner.
la source
Vous devriez éviter les variables globales comme la peste .
Je ne mettrais pas une limite difficile à nombre d'arguments (comme 3 ou 4), mais vous ne voulez les garder au minimum, si possible.
Utilisez
struct
s (ou des objets en C ++) pour regrouper des variables dans une seule entité et les transmettre (par référence) aux fonctions. Habituellement, une fonction reçoit une structure ou un objet (contenant quelques éléments différents), ainsi que deux autres paramètres indiquant à la fonction de faire quelque chosestruct
.Pour que le code soit modulaire, propre et qui sent bon, essayez de vous en tenir au principe de responsabilité unique . Faites-le avec vos structs (ou objets), les fonctions et les fichiers source. Si vous faites cela, le nombre naturel de paramètres passés à une fonction sera évident.
la source
Nous parlons de charge cognitive, pas de syntaxe. La question est donc ... Qu'est - ce qu'un paramètre dans ce contexte?
Un paramètre est une valeur qui affecte le comportement de la fonction. Plus vous avez de paramètres, plus vous obtenez de combinaisons de valeurs possibles, plus le raisonnement sur la fonction est complexe.
En ce sens, les variables globales utilisées par la fonction sont des paramètres. Ce sont des paramètres qui n'apparaissent pas dans sa signature, qui ont des problèmes d'ordre de construction, de contrôle d'accès et de rémanence.
À moins que ce paramètre ne soit considéré comme une préoccupation transversale , c'est-à-dire un état à l'échelle du programme que tout utilise mais que rien ne change (par exemple un objet de journalisation), vous ne devez pas remplacer les paramètres de fonction par des variables globales. Ils seraient toujours des paramètres, mais plus méchants.
la source
IMHO votre question est basée sur un malentendu. Dans "Clean Code", Bob Martin ne suggère pas de remplacer les paramètres de fonction répétés par des globaux, ce serait un conseil vraiment affreux. Il suggère de les remplacer par des variables membres privées de la classe de la fonction. Et il propose également de petites classes cohérentes (généralement plus petites que les 600 lignes de code que vous avez mentionnées), de sorte que ces variables membres ne sont définitivement pas des globales.
Ainsi, lorsque vous avez l’opinion dans un contexte comportant moins de 600 lignes "l’utilisation de variables globales en vaut la peine" , vous partagez parfaitement l’opinion de Oncle Bob. Bien entendu, il est discutable de choisir le nombre idéal de "3 paramètres au maximum" et si cette règle entraîne parfois un trop grand nombre de variables membres, même dans de petites classes. IMHO c'est un compromis, il n'y a pas de règle absolue où tracer la ligne.
la source
Avoir beaucoup de paramètres est considéré comme indésirable, mais les transformer en champs ou en variables globales est bien pire car cela ne résout pas le problème réel mais introduit de nouveaux problèmes.
Avoir de nombreux paramètres n’est pas en soi le problème, mais c’est un symptôme que vous pourriez avoir un problème. Considérez cette méthode:
Avoir 7 paramètres est un signe avant-coureur. Le problème sous-jacent est que ces paramètres ne sont pas indépendants mais appartiennent à des groupes.
left
ettop
appartiennent ensemble en tant quePosition
structure,length
et enheight
tant queSize
structure, etred
,blue
et engreen
tant queColor
structure. Et peutColor
- être que la transparence appartient à uneBrush
structure? PeutPosition
- être etSize
appartient-il dans uneRectangle
structure, auquel cas nous pouvons même envisager de la transformer en unePaint
méthode sur l'Rectangle
objet à la place? Donc on peut se retrouver avec:Mission accomplie! Mais l’important est que nous avons réellement amélioré la conception globale et que la réduction du nombre de paramètres en est une conséquence . Si nous réduisons simplement le nombre de paramètres sans aborder les problèmes sous-jacents, nous pourrions faire quelque chose comme ceci:
Ici, nous avons obtenu la même réduction du nombre de paramètres, mais nous avons en fait aggravé la conception .
Conclusion: pour tout conseil en matière de programmation et règles empiriques, il est vraiment important de comprendre le raisonnement sous-jacent.
la source
ne pas
Avez-vous lu le reste du livre?
Un global est juste un paramètre caché. Ils causent une douleur différente. Mais ça fait toujours mal. Arrêtez de penser aux moyens de contourner cette règle. Pensez à la façon de le suivre.
Qu'est-ce qu'un paramètre?
C'est des trucs. Dans une boîte bien étiquetée. Pourquoi est-ce important de savoir combien de boîtes vous avez lorsque vous pouvez y mettre ce que vous voulez?
C'est le coût d'expédition et de manutention.
Dis-moi que tu peux lire ça. Allez, essaie.
C'est pourquoi.
Cette astuce s'appelle introduire un paramètre objet .
Et oui, c'est juste un truc si vous ne faites que compter les paramètres. Ce que vous devriez compter, ce sont des idées! Abstractions! A quel point me fais-tu penser à la fois? Rester simple.
Maintenant, c'est le même compte:
OW! C'est horrible! Qu'est-ce qui s'est mal passé ici?
Il ne suffit pas de limiter le nombre d'idées. Ils doivent être des idées claires. Qu'est-ce que c'est que le xyx?
Maintenant c'est encore faible. Quelle est la meilleure façon de penser à cela?
Pourquoi faire en sorte que la fonction fasse plus que ce dont elle a réellement besoin? Le principe de responsabilité unique ne concerne pas que les cours. Sérieusement, cela vaut la peine de changer toute une architecture pour éviter qu'une fonction ne devienne un cauchemar surchargé de 10 paramètres parfois liés, dont certains ne peuvent pas être utilisés avec d'autres.
J'ai vu le code où le nombre le plus commun de lignes dans une fonction était 1. Sérieusement. Je ne dis pas que vous devez écrire de cette façon, mais sheesh, ne me dites pas qu'un monde est la SEULE façon de respecter cette règle. Arrêtez d'essayer de sortir de refactoriser cette fonction correctement. Vous savez que vous pouvez. Cela pourrait se décomposer en quelques fonctions. Il pourrait en fait se transformer en quelques objets. Enfer, vous pourriez même en casser une partie dans une application totalement différente.
Le livre ne vous dit pas de compter vos paramètres. Cela vous dit de faire attention à la douleur que vous causez. Tout ce qui résout le problème résout le problème. Sachez simplement que vous échangez simplement une douleur contre une autre.
la source
Je n'utiliserais jamais de variables globales pour réduire les paramètres. La raison en est que les fonctions globales peuvent être modifiées par n’importe quelle fonction / commande, ce qui rend l’entrée de la fonction peu fiable et sujette à des valeurs hors de ce que la fonction peut gérer. Et si la variable était modifiée pendant l'exécution de la fonction et que la moitié de la fonction avait des valeurs différentes de celles de l'autre moitié?
En revanche, le passage de paramètres limite la portée de la variable à sa propre fonction, de sorte que seule la fonction peut modifier un paramètre une fois qu'elle est appelée.
Si vous devez transmettre des variables globales au lieu d'un paramètre, il est préférable de redéfinir le code.
Juste mes deux cents.
la source
Je suis avec Oncle Bob à ce sujet et je suis d'accord pour dire qu'il faut éviter plus de 3 paramètres (j'utilise très rarement plus de 3 paramètres dans une fonction). Avoir beaucoup de paramètres sur une seule fonction crée un problème de maintenance plus important et donne probablement à penser que votre fonction en fait trop / a trop de responsabilités.
Si vous utilisez plus de 3 dans une méthode dans un langage OO, vous devriez considérer que les paramètres ne sont pas liés les uns aux autres d'une manière ou d'une autre et que vous devriez donc plutôt passer un objet à la place.
De plus, si vous créez plus de fonctions (plus petites), vous remarquerez également que les fonctions ont tendance à avoir plus souvent 3 paramètres ou moins. La fonction / méthode d'extraction est votre amie :-).
N'utilisez pas de variables globales pour éviter d'avoir plus de paramètres! C'est échanger une mauvaise pratique contre une pire encore!
la source
Une alternative valable à de nombreux paramètres de fonction consiste à introduire un objet paramètre . Ceci est utile si vous avez une méthode composée qui passe (presque) tous ses paramètres à un tas d'autres méthodes.
Dans les cas simples, il s’agit d’un simple DTO n’ayant que les anciens paramètres comme propriétés.
la source
L'utilisation de variables globales semble toujours un moyen facile de coder (surtout dans un petit programme), mais cela rendra votre code difficile à étendre.
Oui, vous pouvez réduire le nombre de paramètres dans une fonction en utilisant un tableau pour lier les paramètres dans une entité.
La fonction ci-dessus sera modifiée et remplacée par [using associative array] -
Je suis d'accord avec la réponse de robert bristow-johnson : vous pouvez même utiliser une structure pour lier des données dans une seule entité.
la source
En prenant un exemple tiré de PHP 4, regardez la signature de la fonction pour
mktime()
:int mktime ([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )
Ne trouvez-vous pas cela déroutant? La fonction s'appelle "make time" mais elle prend des paramètres de jour, mois et année ainsi que trois paramètres de temps. Est-il facile de se souvenir de l'ordre dans lequel ils vont? Et si tu vois
mktime(1, 2, 3, 4, 5, 2246);
? Pouvez-vous comprendre cela sans avoir à vous référer à autre chose? Est2246
interprété comme un temps de 24 heures "22:46"? Que signifient les autres paramètres? Ce serait mieux comme objet.En passant à PHP 5, il y a maintenant un objet DateTime. Parmi ses méthodes sont deux appelés
setDate()
etsetTime()
. Leurs signatures sont les suivantes:public DateTime setDate ( int $year , int $month , int $day )
public DateTime setTime ( int $hour , int $minute [, int $second = 0 ] )
Vous devez toujours vous rappeler que l'ordre des paramètres va du plus grand au plus petit, mais c'est une grande amélioration. Notez qu’aucune méthode unique ne vous permet de définir les six paramètres en même temps. Vous devez faire deux appels séparés pour le faire.
Ce dont parle Oncle Bob, c'est d'éviter d'avoir une structure plate. Les paramètres associés doivent être regroupés dans un objet. Si vous avez plus de trois paramètres, il est fort probable que vous ayez un pseudo-objet, ce qui vous donne la possibilité de créer un objet approprié pour une plus grande séparation. Bien que PHP ne dispose pas d' un séparé
Date
etTime
classe, vous pouvez considérer queDateTime
contient vraiment unDate
objet et unTime
objet.Vous pourriez éventuellement avoir la structure suivante:
Est-il obligatoire de définir deux ou trois paramètres à chaque fois? Si vous voulez changer l'heure ou le jour, il est maintenant facile de le faire. Oui, l'objet doit être validé pour s'assurer que chaque paramètre fonctionne avec les autres, mais c'était le cas avant.
La chose la plus importante est: est-ce plus facile à comprendre et donc à maintenir? Le bloc de code en bas est plus gros qu'une seule
mktime()
fonction, mais je dirais que c'est beaucoup plus facile à comprendre; même un non-programmeur n'aurait pas beaucoup de mal à comprendre ce qu'il fait. L’objectif n’est pas toujours un code plus court ou plus intelligent, mais un code plus facile à gérer.Oh, et n'utilise pas de globals!
la source
Beaucoup de bonnes réponses ici mais la plupart ne répondent pas à la question. Pourquoi ces règles empiriques? Il s’agit de la portée, des dépendances et de la modélisation appropriée.
Les arguments globaux pour les arguments sont pires car il semble que vous ayez simplifié les choses tout en dissimulant la complexité. Vous ne le voyez plus dans le prototype, mais vous devez tout de même en prendre conscience (ce qui est difficile, car il n'est plus à votre disposition pour le voir) et il ne vous sera pas utile de comprendre la logique de la fonction. Soyez une autre logique cachée qui intervient derrière votre dos. Votre portée est allée partout et vous avez introduit une dépendance sur quoi que ce soit parce que tout peut maintenant jouer avec votre variable. Pas bon.
La principale chose à maintenir pour une fonction est que vous pouvez comprendre ce qu’elle fait en regardant le prototype et l’appel. Donc, le nom doit être clair et sans ambiguïté. Mais aussi, plus il y a d'arguments, plus il sera difficile de comprendre ce qu'il fait. Cela élargit la portée dans votre tête, trop de choses se passent, c'est pourquoi vous voulez limiter le nombre. Peu importe le type d'arguments à traiter, certains sont pires que d'autres. Un booléen optionnel supplémentaire qui permet un traitement insensible à la casse ne rend pas la fonction plus difficile à comprendre, vous ne voudriez donc pas en faire tout un plat. En note de bas de page, les enums font de meilleurs arguments que les booléens, car leur signification est évidente dans l'appel.
Le problème typique n’est pas que vous écriviez une nouvelle fonction avec une énorme quantité d’arguments, vous ne commencerez avec quelques-uns que lorsque vous ne réaliserez pas encore à quel point le problème que vous résolvez est complexe. Au fur et à mesure que votre programme évolue, les listes d'arguments ont tendance à s'allonger progressivement. Une fois qu'un modèle est défini dans votre esprit, vous souhaitez le conserver car il s'agit d'une référence sûre que vous connaissez. Mais, rétrospectivement, le modèle n’est peut-être pas si bon. Vous avez manqué une étape ou trop dans la logique et vous n'avez pas reconnu une entité ou deux. "OK ... je pourrais recommencer et passer un jour ou deux à refactoriser mon code ou ... je pourrais ajouter cet argument pour que je puisse enfin faire ce qu'il faut et en finir. Pour l'instant. Pour ce scénario Pour éliminer ce bogue de mon assiette afin que je puisse déplacer le pense-bête à fini. "
Plus vous utilisez souvent la deuxième solution, plus la maintenance sera coûteuse et plus le refactor deviendra difficile et peu attrayant.
Il n’existe pas de solution de plaque de chaudière permettant de réduire le nombre d’arguments dans une fonction existante. Le simple fait de les regrouper en composés ne facilite pas vraiment les choses, c'est simplement un autre moyen de supprimer la complexité sous le tapis. Faire les choses correctement implique de regarder à nouveau la pile d'appels et de reconnaître ce qui manque ou a été mal fait.
la source
Plusieurs fois, j'ai constaté que le regroupement de nombreux paramètres envoyés ensemble améliorait les choses.
Cela vous oblige à donner un bon nom à cette agrupation de concepts utilisés ensemble. Si vous utilisez ces paramètres ensemble, il se peut qu'ils aient une relation (ou que vous ne deviez pas les utiliser ensemble).
Une fois avec l'objet, je trouve habituellement que certaines fonctionnalités peuvent être déplacées par rapport à cet objet apparemment stuypide. Il est généralement facile à tester et avec une grande cohésion et un faible couplage, ce qui rend la chose encore meilleure.
La prochaine fois que je devrai ajouter un nouveau paramètre, je pourrai l’inclure sans changer la signature de nombreuses méthodes.
Cela peut donc ne pas fonctionner 100% du temps, mais demandez-vous si cette liste de paramètres doit être groupée dans un objet. Et s'il vous plaît. N'utilisez pas de classes non typées comme Tuple pour éviter de créer l'objet. Vous utilisez une programmation orientée objet, cela ne vous gêne pas de créer plus d’objets si vous en avez besoin.
la source
Avec tout le respect que je vous dois, je suis presque certain que vous avez complètement manqué le point d'avoir un petit nombre de paramètres dans une fonction.
L'idée est que le cerveau ne peut contenir qu'un maximum d'informations " actives" à la fois, et si vous avez n paramètres dans une fonction, vous avez n autres éléments "d'informations actives" qui doivent être dans votre cerveau pour pouvoir fonctionner facilement et avec précision. comprendre ce que fait le morceau de code (Steve McConnell, dans Code Complete (un livre bien meilleur, IMO), dit quelque chose de similaire à propos de 7 variables dans le corps d’une méthode: nous atteignons rarement cela, mais plus et vous perdez la garder tout droit dans la tête).
L’intérêt d’un petit nombre de variables est de pouvoir garder les exigences cognitives associées à l’utilisation de ce code afin que vous puissiez y travailler (ou le lire) plus efficacement. Une cause secondaire de ceci est que le code bien factorisé aura tendance à avoir de moins en moins de paramètres (par exemple, un code mal factorisé a tendance à grouper un tas de choses dans un fouillis).
En passant des objets au lieu de valeurs, vous gagnerez peut-être un niveau d'abstraction pour votre cerveau, car il doit maintenant comprendre que j'ai un contexte de recherche à utiliser, au lieu de penser aux 15 propriétés pouvant se trouver dans ce contexte de recherche.
En essayant d'utiliser des variables globales, vous êtes complètement allé dans la mauvaise direction. Maintenant, non seulement vous n’avez pas résolu le problème de trop de paramètres pour une fonction, vous l’avez pris et l’avez jeté dans un bien meilleur problème que vous devez maintenant garder à l’esprit!
Désormais, au lieu de travailler uniquement au niveau de la fonction, vous devez également prendre en compte la portée globale de votre projet (bâillement, quelle horreur! Je frémis ...). Vous n'avez même pas toutes les informations devant vous dans la fonction (merde, quel est ce nom de variable globale?) (J'espère que cela n'a pas été modifié par autre chose depuis que j'ai appelé cette fonction).
Les variables globales constituent l'une des pires choses à voir dans un projet (grand ou petit). Ils dénotent un handicap pour une bonne gestion de la portée, ce qui est une compétence extrêmement fondamentale pour la programmation. Ils. Sont. Mal.
En déplaçant des paramètres hors de votre fonction et en les plaçant dans des globales, vous vous êtes tiré dans le sol (ou la jambe, ou le visage). Et Dieu vous préserve jamais de décider que, hé, je peux réutiliser ce global pour autre chose… mon esprit tremble à la pensée.
L'idéal est de garder les choses faciles à gérer, et les variables globales ne le feront PAS. J'irais même jusqu'à dire que c'est l'une des pires choses que vous puissiez faire pour aller dans la direction opposée.
la source
J'ai trouvé une approche très efficace (en JavaScript) pour minimiser les frictions entre interfaces: Utilisez une interface uniforme pour tous les modules , en particulier: les fonctions à un seul paramètre.
Lorsque vous avez besoin de plusieurs paramètres: Utilisez un seul objet / hachage ou tableau.
Ours avec moi, je promets que je ne vais pas traîner ...
Avant de dire «à quoi sert un paramètre param func?» Ou «Existe-t-il une différence entre 1 tableau et les fonctions multi-argument?»
Hé bien oui. C'est peut-être visuellement subtile, mais la différence est multiple - j'explore les nombreux avantages ici
Apparemment, certaines personnes pensent que 3 est le bon nombre d'arguments. Certains pensent que c'est 2. Eh bien, cela pose toujours la question "quel param va dans arg [0]?" Au lieu de choisir l'option qui contraint l'interface avec une déclaration plus rigide.
Je suppose que je préconise une position plus radicale: ne comptez pas sur des arguments de position. Je me sens juste que c'est fragile et conduit à des arguments sur la position de ces maudits arguments. Ignorez-le et continuez tout droit vers l'inévitable bagarre autour des noms de fonctions et de variables. 😉
Sérieusement, une fois que le nom a été réglé, nous espérons que vous obtiendrez le code suivant, qui est quelque peu auto-documenté, non sensible à la position, et permet de gérer les modifications de paramètres futures dans la fonction:
la source