La durée des fonctions affecte-t-elle la productivité d'un programmeur? Si oui, quel est le bon nombre maximum de lignes pour éviter une perte de productivité?
Puisqu'il s'agit d'un sujet très avisé, veuillez sauvegarder la réclamation avec quelques données.
Depuis que je me suis lancé dans cette raquette folle en 1970, j'ai vu exactement un module qui nécessaire pour être plus d'une page imprimée (environ 60 lignes). J'ai vu beaucoup de modules plus longs.
D'ailleurs, j'ai écrit des modules qui étaient plus longs, mais ils étaient généralement de grandes machines à états finis écrites sous la forme de grandes instructions switch.
Une partie du problème semble être que les programmeurs de nos jours ne sont pas appris à modulariser les choses.
Les normes de codage qui maximisent le gaspillage d'espace vertical semblent également faire partie du problème. (J'ai encore pour rencontrer un gestionnaire de logiciel qui a lu Gerald Weinberg " Psychologie de la programmation informatique ". Weinberg souligne que plusieurs études ont montré que la compréhension du programmeur est essentiellement limitée à ce que le programmeur peut voir à un instant donné. Si le programmeur doit faire défiler ou tourner une page, sa compréhension baisse considérablement: il doit se souvenir et abstraire.)
Je reste convaincu que la plupart des gains de productivité du programmeur bien documentés de AVANT étaient dus au système FORTH "block" pour le code source: les modules étaient durs- limité à un maximum absolu de 16 lignes de 64 caractères. Vous pouvez factoriser indéfiniment, mais vous ne pouvez en aucun cas écrire une routine de 17 lignes.
Cela dépend de la langue que vous utilisez, mais en général (et à mon goût personnel):
Si c'est plus, c'est quelque chose que je dois revenir plus tard et retravailler.
Mais de façon réaliste , toute taille qu'il doit être lorsque vous devez livrer quelque chose et qu'il est plus logique de le cracher comme ça, fait il est même parfois plus facile pour quelqu'un de l'examiner avant l'expédition. (mais j'y reviendrai plus tard).
(Récemment, mon équipe a exécuté un programme sur notre base de code: nous avons trouvé une classe avec 197 méthodes et une autre avec seulement 3 méthodes mais l'une d'elles était 600 lignes. Jeu mignon: quel est le pire des 2 maux?)
Maintenant, pour une réponse plus zen ... En général, il est considéré comme une bonne pratique (TM) de citer un ou deux grands hommes, alors voici:
Tout devrait être aussi simple que possible, mais pas plus simple. - A. Einstein
La perfection n'est finalement atteinte pas quand il n'y a plus rien à ajouter, mais quand il n'y a plus rien à emporter. - A. de Saint Exupéry
En complément à cela, vos fonctions doivent avoir des noms clairs expliquant leur intention. Concernant les commentaires, je ne commente généralement pas à l'intérieur d'une fonction:
Un bloc de commentaires en haut de chaque fonction (qui nécessite une explication) suffit. Si votre fonction est petite et que les noms de fonction sont suffisamment explicites, vous devez simplement dire ce que vous voulez réaliser et pourquoi. J'utilise les commentaires en ligne uniquement pour les champs dans certaines langues ou lors du démarrage des blocs pour les fonctions qui enfreignent les règles de 25 à 35 lignes si l'intention n'est pas claire. J'utilise un commentaire de bloc à l'intérieur du code lorsqu'une situation exceptionnelle se produit (un bloc catch où vous n'avez pas besoin ou ne voulez rien faire devrait avoir un commentaire indiquant pourquoi, par exemple).
Pour en savoir plus, veuillez lire ma réponse sur Style et recommandations du code de commentaire
À mon avis, chaque fonction doit être aussi petite que possible. Chaque fonction ne doit faire qu'une seule chose et bien le faire. Cela ne répond pas vraiment à la question de la durée maximale, mais c'est plutôt mes sentiments sur la durée des fonctions.
Pour utiliser les mots de l'oncle Bob, "Extraire jusqu'à ce que vous ne puissiez plus en extraire. Extraire jusqu'à ce que vous tombiez."
Quelle devrait être la hauteur maximale d'un bâtiment? Cela dépend de l'emplacement de la construction ou de la hauteur souhaitée.
Vous pouvez obtenir différentes réponses de différentes personnes venant de différentes villes.
Certaines fonctions de script et gestionnaires d'interruption du noyau sont très longs.
Une méthode qui fonctionne pour moi est: Puis-je avoir une partie d'une fonction plus longue donner un nom qui a du sens. Je pense que la longueur d'une méthode n'est pas aussi importante qu'une bonne dénomination. La méthode doit faire ce que le nom dit, ni plus ni moins. Et vous devriez pouvoir donner un bon nom. Si vous ne pouvez pas nommer votre méthode correctement, le code n'est probablement pas bon.
Tant qu'il faut faire ce qu'il faut, mais pas plus.
Je pense qu'il y a un compromis. Si vous avez beaucoup de méthodes courtes, il est souvent plus difficile de les déboguer qu'une seule méthode longue. Si vous devez parcourir l'éditeur 20 ou 30 fois pour tracer un appel de méthode, il sera difficile de tout garder en tête. En attendant, s'il existe une méthode claire et bien écrite, même si elle est de 100 lignes, il est souvent plus facile de la garder en tête.
La vraie question est de savoir pourquoi les éléments devraient être dans des méthodes différentes, et la réponse donnée ci-dessus est la réutilisation du code. Si vous ne réutilisez pas le code (ou ne savez pas), il peut être judicieux de le laisser dans une méthode géante facile à suivre, puis comme vous devez le réutiliser, divisez les parties qui ont besoin d'être en utilisant des méthodes plus petites.
En réalité, une bonne conception des méthodes consiste à créer des méthodes fonctionnellement cohérentes (elles font essentiellement une chose). La longueur des méthodes n'a pas d'importance. Si une fonction fait une chose bien définie et fait 1 000 lignes, c'est une bonne méthode. Si une fonction fait 3 ou 4 choses et ne fait que 15 lignes, alors c'est une mauvaise méthode ...
De Complexité cyclomatique (Wikipedia):
La complexité cyclomatique d'une section de code source est le décompte du nombre de chemins linéairement indépendants à travers le code source.
Je vous recommande de garder ce nombre sous 10 dans une seule méthode. S'il arrive à 10, alors il est temps de refactoriser.
Il existe des outils qui peuvent évaluer votre code et vous donner un numéro de complexité cyclomatique.
Vous devez vous efforcer d'intégrer ces outils dans votre pipeline de build.
Ne poursuivez pas littéralement une taille de méthode, mais essayez de regarder sa complexité et ses responsabilités. S'il a plus d'une responsabilité, c'est probablement une bonne idée de refactoriser. Si sa complexité cyclomatique augmente, il est probablement temps de re-factoriser.
Je suis assez certain qu'il existe d'autres outils qui vous donnent des commentaires similaires, mais je n'ai pas encore eu l'occasion d'examiner cela.
Pour moi, une fonction est toute longueur qu'elle doit être. La plupart du temps, je le partage quand je réutilise le code.
Fondamentalement, je m'en tiendrai au principe "haute cohésion, faible couplage" et il n'y a aucune contrainte de longueur.
La question devrait être de savoir combien de choses une fonction devrait faire. Et généralement, il est rare que vous ayez besoin de 100 lignes pour faire "une" chose. Encore une fois, cela dépend du niveau à partir duquel vous regardez le code: le hachage d'un mot de passe est-il une chose? Ou le hachage et la sauvegarde du mot de passe sont-ils une chose?
Je dirais, commencez par enregistrer le mot de passe comme une fonction. Lorsque vous sentez que le hachage est différent et que vous refactorisez le code. Je ne suis en aucun cas un programmeur expert, mais à mon humble avis, l'idée des fonctions commence petit est que plus vos fonctions sont atomiques, plus les chances de réutilisation du code sont élevées, sans jamais avoir à faire le même changement à plus d'un endroit , etc.
J'ai vu SQL procédures stockées qui fonctionnent sur 1000 lignes. Le nombre de lignes de procédures stockées est-il également inférieur à 50? Je ne sais pas, mais ça rend la lecture du code, diable. Non seulement vous devez continuer à faire défiler vers le haut ou vers le bas, vous devez donner à quelques lignes de code un nom comme "ceci fait la validation1", "cette mise à jour dans la base de données", etc. - un travail que le programmeur aurait dû faire.
Je trouve plus facile de garder une trace de ce que je fais si je peux voir la fonction entière d'un coup. Voici donc comment je préfère écrire des fonctions:
J'écris rarement des fonctions plus longtemps que ça. La plupart d'entre elles sont des instructions de commutation C/C++ géantes.
assez court pour être optimisé correctement
Les méthodes doivent être assez courtes pour faire exactement une chose. La raison en est simple: afin que votre code puisse être correctement optimisé.
Dans un langage JIT comme Java ou C #, il est important que vos méthodes soient simples pour que le compilateur JIT puisse produire du code rapidement. Les méthodes plus longues et plus compliquées nécessitent naturellement plus de temps JIT). De plus, les compilateurs JIT n'offrent qu'une poignée d'optimisations et seules les méthodes les plus simples en bénéficient, ce qui a même été évoqué dans le C # effectif de Bill Wagner C # .
Dans un langage de niveau inférieur, comme C ou C++, avoir des méthodes courtes (peut-être une douzaine de lignes environ) est également important parce que de cette façon vous minimisez le besoin de stocker des variables locales dans RAM plutôt que dans un registre. (Aka 'Register Spilling'.) Notez cependant que dans ce cas non géré, le coût relatif de chaque appel de fonction peut être assez élevé.
Et même dans un langage dynamique, comme Ruby ou Python, avoir des méthodes courtes aide également dans les optimisations du compilateur. Dans un langage dynamique, plus une fonctionnalité est "dynamique", plus il est difficile de Par exemple, une méthode longue qui prend un X et pourrait renvoyer un Int, Float ou String exécutera probablement beaucoup plus lentement que trois méthodes distinctes qui ne renvoient chacune qu'un seul type. En effet, si le compilateur sait exactement de quel type la fonction reviendra, elle peut également optimiser le site d'appel de la fonction (par exemple, ne pas vérifier les conversions de type).
En règle générale, j'essaie de garder mes méthodes/fonctions à ce qui tient sur l'écran d'un moniteur 1680x1050. Si cela ne convient pas, utilisez des méthodes/fonctions d'assistance pour répartir la tâche.
Il aide à la lisibilité à la fois sur écran et sur papier.
Je ne mets aucune limite stricte sur quoi que ce soit, car certaines fonctions implémentent des algorithmes intrinsèquement complexes et toute tentative de les raccourcir rendrait les interactions entre les nouvelles fonctions plus courtes si compliquées que le résultat net ne serait pas une réduction de la simplicité. Je ne crois pas non plus que l'idée qu'une fonction ne fasse que "une chose" soit un bon guide, car "une chose" à un niveau d'abstraction élevé peut être "beaucoup de choses" à un niveau inférieur.
Pour moi, une fonction est définitivement trop longue si sa longueur provoque des violations subtiles de DRY en ce moment, et l'extraction d'une partie de la fonction dans une nouvelle fonction ou classe pourrait résoudre ce problème. Une fonction peut être trop longtemps si ce n'est pas le cas, mais une fonction ou une classe pourrait facilement être extraite, ce qui rendrait le code plus modulaire d'une manière susceptible d'être utile face aux changements prévisibles en cours de route.
Cela dépend beaucoup du contenu du code.
J'ai vu une routine de mille lignes avec laquelle je n'ai eu aucun problème. C'était une déclaration de commutateur énorme, aucune option ne dépassait une douzaine de lignes et la seule structure de contrôle dans une option était une seule boucle. Ces jours-ci, il aurait été écrit avec des objets, mais ce n'était pas une option à l'époque.
Je regarde également 120 lignes dans un commutateur devant moi. Aucun cas ne dépasse 3 lignes - un gardien, une affectation et la pause. C'est l'analyse d'un fichier texte, les objets ne sont pas une possibilité. Toute alternative serait plus difficile à lire.
La plupart des compilateurs ne se soucient pas de la longueur d'une fonction. Une fonction doit être fonctionnelle, mais être à la fois facile à comprendre, à changer et à réutiliser pour les êtres humains. Choisissez une longueur qui vous convient le mieux.
Peut-être que la longueur de la fonction n'est pas une bonne mesure. Nous essayons d'utiliser complexité cyclomatique , sur les méthodes aussi, et l'une des futures règles de contrôle de source que la complexité cyclomatique sur les classes et les méthodes doit être inférieure à X.
Pour les méthodes, X est défini sur 30, et c'est assez serré.
Ma règle générale est qu'une fonction doit tenir sur l'écran. Il n'y a que trois cas que j'ai trouvés qui ont tendance à violer cela:
1) Fonctions de répartition. Dans le passé, cela était courant, mais la plupart d'entre eux sont remplacés par l'héritage d'objets de nos jours. Cependant, les objets ne fonctionnent qu'à l'intérieur de votre programme et vous verrez donc des fonctions de répartition occasionnelles lorsque vous traitez des données provenant d'ailleurs.
2) Fonctions qui effectuent tout un tas d'étapes pour atteindre un objectif et où les étapes manquent d'une bonne subdivision. Vous vous retrouvez avec une fonction qui appelle simplement une longue liste d'autres fonctions dans l'ordre.
3) Comme le numéro 2, mais où les étapes individuelles sont si petites qu'elles sont simplement alignées plutôt qu'appelées séparément.