web-dev-qa-db-fra.com

Techniques pour minimiser le nombre d'arguments de fonction

Dans Clean Code, il est écrit que "le nombre idéal d'arguments pour une fonction est zéro". Les raisons sont expliquées et ont un sens. Ce que je recherche, ce sont des techniques pour refactoriser les méthodes avec 4 arguments ou plus pour résoudre ce problème.

Une façon est d'extraire les arguments dans une nouvelle classe, mais cela conduirait sûrement à une explosion de classes? Et ces classes sont susceptibles de se retrouver avec des noms qui violent certaines des règles de dénomination (se terminant par "Data" ou "Info" etc.)?

Une autre technique consiste à faire des variables utilisées par plusieurs fonctions une variable membre privée pour éviter de les transmettre, mais cela étend la portée de la variable, éventuellement de telle sorte qu'elle soit ouverte aux fonctions qui n'en ont pas réellement besoin.

Je cherche juste des moyens de minimiser les arguments de fonction, après avoir accepté les raisons pour lesquelles c'est une bonne idée de le faire.

13
Neil Barnwell

La chose la plus importante à retenir est que ce sont des lignes directrices, pas des règles.

Il y a des cas où une méthode simplement doit prendre un argument. Pensez au + méthode pour les nombres, par exemple. Ou la méthode add pour une collection.

En fait, on pourrait même soutenir que ce que cela signifie d'ajouter deux nombres dépend du contexte, par exemple dans ℤ 3 + 3 == 6, mais en ℤ| 5 3 + 3 == 2, donc vraiment l'opérateur d'addition devrait être une méthode sur un objet contextuel qui prend deux arguments au lieu d'une méthode sur les nombres qui prend un argument.

De même, une méthode de comparaison de deux objets doit être soit une méthode d'un objet prenant l'autre comme argument, soit une méthode du contexte, prenant deux objets en argument, donc cela n'a tout simplement pas de sens d'avoir une méthode de comparaison avec moins d'un argument.

Cela dit, il y a quelques choses qui peuvent être faites pour réduire le nombre d'arguments pour une méthode:

  • Rendre la méthode elle-même plus petite: Peut-être que si la méthode a besoin d'autant d'arguments, elle en fait trop?
  • ne abstraction manquante: Si les arguments sont étroitement corrélés, peut-être qu'ils vont ensemble et qu'il y a une abstraction qui vous manque? (Exemple de manuel canonique: au lieu de deux coordonnées, passez un objet Point, ou au lieu de transmettre un nom d'utilisateur et un e-mail, passez un objet IdCard.)
  • Object object: Si l'argument est nécessaire par plusieurs méthodes, il devrait peut-être faire partie de l'état de l'objet. S'il n'est nécessaire que par certaines des méthodes mais pas par d'autres, peut-être que l'objet en fait trop et devrait vraiment être deux objets.

Une façon est d'extraire les arguments dans une nouvelle classe, mais cela conduirait sûrement à une explosion de classes?

Si votre modèle de domaine comporte de nombreux types de choses, votre code se retrouvera avec de nombreux types d'objets différents. Il n'y a rien de mal à cela.

Et ces classes sont susceptibles de se retrouver avec des noms qui violent certaines des règles de dénomination (se terminant par "Data" ou "Info" etc.)?

Si vous ne trouvez pas un nom correct, vous avez peut-être regroupé trop d'arguments ou trop peu. Donc, soit vous avez juste un fragment d'une classe, soit vous avez plus d'une classe.

Une autre technique consiste à faire des variables utilisées par plusieurs fonctions une variable membre privée pour éviter de les transmettre, mais cela étend la portée de la variable, éventuellement de telle sorte qu'elle soit ouverte aux fonctions qui n'en ont pas réellement besoin.

Si vous avez un groupe de méthodes qui fonctionnent toutes sur les mêmes arguments, et un autre groupe de méthodes qui ne le font pas, elles appartiennent peut-être à des classes différentes.

Notez combien de fois j'ai utilisé le mot "peut-être"? C'est pourquoi ce sont des lignes directrices, pas des règles. Peut-être que votre méthode avec 4 paramètres est parfaitement bien!

16
Jörg W Mittag

Notez que zéro argument n'implique pas d'effets secondaires, car votre objet est un argument implicite. Regardez combien de méthodes sans arité la liste immuable de Scala a, par exemple.

Une technique utile que j'appelle la technique de "focalisation de l'objectif". Lorsque vous effectuez la mise au point d'un objectif d'appareil photo, il est plus facile de voir le vrai point de mise au point si vous le prenez trop loin, puis de le reculer au bon point. Il en va de même pour le refactoring logiciel.

Surtout si vous utilisez le contrôle de version distribué, les modifications logicielles sont faciles à expérimenter, voyez si vous aimez leur apparence et reculez si vous ne le faites pas, mais pour une raison quelconque, les gens semblent souvent réticents à le faire.

Dans le contexte de votre question actuelle, cela signifie écrire les versions à zéro ou à un argument, avec plusieurs fonctions séparées d'abord, puis il est relativement facile de voir quelles fonctions doivent être combinées pour plus de lisibilité.

Notez que l'auteur est également un ardent défenseur du développement piloté par les tests, qui a tendance à produire des fonctions de faible densité au début, car vous commencez avec vos cas de test triviaux.

6
Karl Bielefeldt

Une approche qui simplement (et naïvement - ou devrais-je même dire aveuglément) vise simplement à réduire le nombre d'arguments aux fonctions est probablement fausse. Il n'y a absolument rien de mal avec les fonctions ayant un grand nombre d'arguments. S'ils sont requis par la logique, eh bien ils sont nécessaires ... Une longue liste de paramètres ne m'inquiète pas du tout - tant qu'elle est correctement formatée et commentée pour la lisibilité.

Dans le cas où tous ou un sous-ensemble d'arguments appartiennent à une entité logique unique et sont généralement transmis en groupes dans tout votre programme, il peut-être est logique de les regrouper dans un conteneur - généralement une structure ou une autre objet. Des exemples typiques peuvent être une sorte de type de données message ou event.

Vous pouvez facilement exagérer cette approche - une fois que vous constatez que l'emballage et le déballage de trucs vers et depuis de tels conteneurs de transport génèrent plus de surcharge que cela améliore la lisibilité, vous êtes probablement allé trop loin.

OTOH, de grandes listes de paramètres peuvent être un signe que votre programme peut ne pas être correctement structuré - peut-être la fonction qui nécessite un si grand nombre de paramètres essaie juste d'en faire trop et doit être divisée en plusieurs plus petits les fonctions. Je préfère commencer ici que de me soucier du nombre de paramètres.

0
tofro

Il n'y a pas de moyen magique: vous devez maîtriser le domaine problématique pour découvrir la bonne architecture. C'est le seul moyen de refactoriser: maîtriser le domaine problématique. Plus de quatre paramètres est juste un pari sûr que votre architecture actuelle est défectueuse et erronée.

Les seules exceptions sont les compilateurs (métaprogrammes) et les simulations, où la limite est théoriquement 8, mais probablement seulement 5.

0
theDoctor