Qu'est-ce qui est prouvé comme une bonne longueur maximale d'une fonction? [fermé]


44

La longueur de la fonction affecte-t-elle la productivité d'un programmeur? Si tel est le cas, quel est le bon nombre maximal de lignes pour éviter une perte de productivité?

Puisqu'il s'agit d'un sujet très controversé, veuillez sauvegarder la réclamation avec certaines données.


6
La longueur ne doit pas être mesurée en LDC, mais en fonction du temps nécessaire pour comprendre précisément ce qu’elle fait. Et cette durée ne devrait pas dépasser une minute. Si je n'arrive pas à comprendre en quelques secondes, c'est probablement que j'en fais trop ... après une minute, c'est définitivement le cas.
CaffGeek


13
La longueur maximale devrait être de 17.
ThomasX

1
Pensez S dans SOLID.
Kris Krause

1
@CaffGeek Ou peut-être que la fonction fait simplement quelque chose de non trivial. J'ai vu des fonctions qui me prendraient des jours pour bien comprendre. Même les fonctions dans lesquelles je comprends tous les concepts impliqués peuvent prendre facilement une demi-heure pour régler les détails. Bien qu'il soit agréable d'avoir des fonctions triviales, de nombreux problèmes sont simplement difficiles en soi.
CodesInChaos

Réponses:


46

Depuis que je me suis embarqué dans cette folle raquette en 1970, j'ai vu exactement un module qui devait vraiment comporter plus d'une page imprimée (environ 60 lignes). J'ai vu beaucoup de modules plus longs.

D'ailleurs, j'ai écrit des modules plus longs, mais c'étaient généralement de grandes machines à états finis écrites sous forme de grandes instructions switch.

Une partie du problème semble être que les programmeurs n’ont pas appris à modéliser les choses.

Les normes de codage qui maximisent le gaspillage de l’espace vertical semblent également faire partie du problème. (Je n'ai pas encore rencontré un responsable de logiciel qui a lu la " Psychologie de la programmation informatique " de Gerald Weinberg . Weinberg souligne que de nombreuses études ont montré que la compréhension du programmeur est essentiellement limitée à ce que le programmeur peut voir à un instant donné. le programmeur doit faire défiler ou tourner une page, sa compréhension diminue de manière significative: il doit se rappeler et résumer.)

Je reste convaincu que les gains de productivité bien documentés de FORTH, bien documentés, sont dus au système de "bloc" FORTH pour code source: les modules étaient limités à 16 lignes de 64 caractères. Vous pouvez factoriser à l'infini, mais vous ne pouvez en aucun cas écrire une routine de 17 lignes.


3
Toute la philosophie de FORTH visait à encourager cela ... Vous vouliez concevoir votre propre vocabulaire, en divisant sans cesse votre programme en morceaux de plus en plus petits, pour vous retrouver avec moins de script et plus de dictionnaire. Les limites de longueur à elles seules ne le font pas - vous verrez une routine scindée en plusieurs parties uniquement pour satisfaire à certaines normes de codage. Je pense que vous avez tout à fait raison de penser que les programmeurs n’ont tout simplement pas "appris à modulariser les choses"; cela aurait dû être l’une des grandes victoires de la programmation orientée objet, mais pour diverses raisons, il est souvent minimisé comme objectif à atteindre.
Shog9

1
@M. CRT: limites de longueur dans les implémentations FORTH orientées bloc. FORCED modularization. La nature interactive de la plupart des FORTH aide en encourageant les petits modules et en testant rapidement ces modules. D85, un fichier FORTH basé sur des fichiers, n’a pas forcé la modularisation, et j’ai vu des joueurs jouer avec D85 écrire beaucoup de modules pour toujours avec lui. D'où ma conviction. (Pour ce que ça vaut, Liz Plutôt n'est pas d'accord avec moi. Elle pense que c'est principalement l'interactivité qui donne à FORTH le gain de productivité.)
John R. Strohm

2
+1 pour me présenter le grand livre "Psychology of Computer Programming" :)
Samuel, le

30

Quelle est la bonne taille, vraiment?

Cela dépend de la langue que vous utilisez, mais en général (et à mon goût personnel):

  • Idéalement , moins de 25 lignes.
  • Acceptable , moins de 35 lignes.

Si c'est plus, alors c'est quelque chose que je dois revenir plus tard et retravailler.

Mais, de manière réaliste , quelle que soit la taille requise pour livrer quelque chose et qu'il est plus logique de la répartir ainsi, il est parfois encore plus facile pour quelqu'un de vérifier avant expédition. (mais reviens quand même plus tard).

(Récemment, mon équipe a lancé 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 de 600 lignes. Cute game: 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 de citer un ou deux grands hommes, alors voici:

Tout devrait être rendu aussi simple que possible, mais pas plus simple. - A. Einstein

La perfection est finalement atteinte, non pas lorsqu'il n'y a plus rien à ajouter, mais lorsqu'il n'y a plus rien à enlever. - A. de Saint Exupéry


Addendum sur les styles de commentaires

En plus de cela, vos fonctions devraient avoir des noms clairs expliquant leur intention. En ce qui concerne les commentaires, généralement, je ne commente pas dans une fonction:

  • les commentaires disent "pourquoi?" ,
  • le code dit "comment?" .

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 indiquer ce que vous souhaitez réaliser et pourquoi. J'utilise des commentaires en ligne uniquement pour les champs de certaines langues ou lors du démarrage de blocs pour les fonctions qui ne respectent pas les règles de 25 à 35 lignes si l'intention n'est pas claire. J'utilise un commentaire de bloc dans le code lorsqu'une situation exceptionnelle se produit (un bloc catch pour lequel vous n'avez pas besoin ou ne voulez rien faire devrait comporter un commentaire expliquant pourquoi, par exemple).

Pour plus d'informations, consultez ma réponse sur Style et les recommandations du code de commentaire.


@haylem Je suppose que ceci est la version du programmeur de Freddy contre Jason :-)
Gaurav

Je suis d'accord, mais j'ajouterais que cela devrait être à la taille d'une page d'écran. Si vous avez 72 lignes sur une page d'écran, la fonction ne doit pas dépasser 72 lignes.
Nom à afficher

@ Il faut veiller à ce qu'une seule fonction soit maintenue dans une page d'écran afin de ne pas faire défiler l'écran, mais ce n'est généralement pas ma principale préoccupation. Ce qui me préoccupe, c’est le temps qu’il faut pour traiter les informations lors de la lecture d’une fonction. C’est presque instantané s’il s’agit d’une écriture claire en 25 lignes. Il ne reste plus qu'à suivre les appels de fonction. 72 est beaucoup trop gros pour moi (plus, si vous avez des écrans partagés? Et cela dépend de la police. Mais je suis d'accord pour la valeur historique de la recommandation)
haylem

1
Bien sûr, vous avez parfois des fonctions dans lesquelles vous copiez simplement 70 champs différents dans 70 endroits différents. Je les utilise principalement ttpour les générer, mais parfois vous êtes bloqué avec une fonction de cul long (ou une fonction de cul longue) qui ne fait vraiment rien d'intéressant de toute façon alors ce n'est pas un vrai problème.
configurateur

1
Lorsque la fonction est, par exemple, Map(x => x.Property1); Map(x => x.Property2); Map(x => x.Property3);il est clair que tout est identique. (Notez que ceci est juste un exemple; ce genre de fonction apparaît de temps en temps)
configurateur

12

À mon avis, chaque fonction devrait être aussi petite que possible. Chaque fonction ne doit faire qu’une chose et le faire bien. Cela ne répond pas vraiment à la question de longueur maximale, mais c’est plus mon sentiment sur la durée des fonctions.

Pour utiliser les mots de Oncle Bob, "Extrait jusqu'à ce que tu ne puisses plus en extraire. Extrait jusqu'à ce que tu tombes."


2
Qu'entendez-vous par aussi petit que possible? Etre aussi petit que possible ne serait-il pas que chaque fonction ait deux lignes: une pour effectuer une seule opération et une autre qui appelle une fonction pour faire le reste?
Kelmikra

Oncle Bob encore. Pense pour toi même. N'écoute pas les oncles. Ils vous égarent.
gnasher729

10

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 personnes différentes venant de différentes villes.
Certaines fonctions de script et certains gestionnaires d’interruptions du noyau sont très longs.


Je suis totalement d'accord avec toi. J'aime les métaphores. :) Un bâtiment de trois étages peut avoir été construit par un architecte fou qui ne sait pas où placer une sortie de sécurité valide. Un autre bâtiment pourrait avoir dix étages et constituer une architecture parfaite. Nous devons toujours garder à l’esprit que la lisibilité et la maintenabilité devraient être la raison principale de la refactorisation d’une méthode visant à réduire sa taille et non de la taille elle-même. Une ville ne pourrait pas être construite avec 90% de gratte-ciel, sauf dans les films de science-fiction. :)
Samuel

10

Une méthode qui fonctionne pour moi est la suivante: 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 devrait faire ce que son nom l'indique, ni plus ni moins. Et vous devriez pouvoir donner un bon nom. Si vous ne pouvez pas nommer bonne votre méthode, le code n'est probablement pas bien mis ensemble.


Et votre méthode ne doit faire qu'une chose pour avoir un bon nom: pas de 'Et', 'Ou' ou toute autre chose qui donne à la méthode un nom de 50 caractères.
Samuel le

10

Tant qu'il faut faire ce qu'il faut faire, mais pas plus longtemps.


comme on dit dans c: "Il n'y a pas de problème dans c que vous ne puissiez pas résoudre en ajoutant un autre pointeur à ce pointeur." vous pouvez toujours ajouter une autre fonction en dessous, sa question serait à votre épiphanie "où finit-il?"
Nom d'affichage

1
En fait, je le retournerais et dirais "aussi court que cela puisse être, mais pas plus court", mais vous obtenez mon +1 pour être assez proche :)
Ben Hughes

6

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 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 dans votre tête. En attendant, s’il existe une méthode claire et bien écrite, même s’il s’agit de 100 lignes, il est souvent plus facile de la garder à l’esprit.

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 de code. Si vous ne réutilisez pas le code (ou si vous ne le savez pas), il peut être judicieux de le laisser dans une méthode simple, facile à suivre, puis, si vous devez le réutiliser, séparez les parties qui ont besoin d'être réutilisées. en utilisant des méthodes plus petites.

En réalité, une bonne conception de méthode consiste à créer des méthodes fonctionnellement cohérentes (essentiellement, elles ne font qu'une chose). La longueur des méthodes n'a pas d'importance. Si une fonction fait une chose bien définie et vaut 1000 lignes, c'est une bonne méthode. Si une fonction fait 3 ou 4 choses et ne compte que 15 lignes, alors c'est une mauvaise méthode ...


J'aime les méthodes courtes.
Marcie

J'aime ce que vous avez dit, car dire qu'une méthode ne devrait pas comporter plus de 10 lignes est à mon sens une utopie. Ok, c’est une bonne règle à garder à l’esprit chaque fois que vous écrivez une méthode, mais cela ne devrait pas être une règle mathématique comme 1 + 1 = 2. Si vous respectez des principes tels que KISS, DRY, YAGNI, etc ... et que vos méthodes sont: pas plein de commentaires expliquant certains détails car trop longs, les méthodes peuvent avoir 100 lignes de code et peuvent être parfaitement propres à comprendre et à maintenir. Cependant, cela devrait être plus une exception qu'une habitude. Je pense que la méthode de commutation en cas d'usine est un bon exemple d'exception.
Samuel le

5

Je trouve plus facile de garder une trace de ce que je fais si je peux voir toute la fonction en une fois. Alors voici comment je préfère écrire des fonctions:

  1. Assez court pour tenir sur mon moniteur avec une police de caractères raisonnable.
  2. S'il doit être plus long que le n ° 1, suffisamment court pour imprimer sur un morceau de papier dans une police appropriée.
  3. S'il doit être plus long que le n ° 2, suffisamment court pour imprimer en 2 poses sur un morceau de papier.

J'écris rarement des fonctions plus longtemps que ça. La plupart sont des déclarations de commutateur géantes C / C ++.


Suffisamment court pour tenir sur un moniteur est bien, mais spécifier le type et la taille de la police. Le papier ne devrait pas être une règle à mon avis, car nous sommes en 2013 et qui imprime toujours du code sur du papier, qui imprimera juste pour voir s'il correspond à un format de papier? Avec des outils tels que Visual Studio, intellisense, il n’ya plus aucune raison d’analyser du code avec du papier.
Samuel le

5

Pour moi, une fonction est une longueur quelconque. La plupart du temps, je le divise lorsque je vais réutiliser du code.

Fondamentalement, je vais m'en tenir au principe 'forte cohésion, couplage faible' et il n'y a pas de contrainte de longueur.


5

La question devrait être 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 auquel vous regardez le code: Est-ce que le hachage d'un mot de passe est une chose? Ou bien le hachage et la sauvegarde du mot de passe sont-ils une chose?

Je dirais, commencez par enregistrer le mot de passe en 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 même des fonctions est modeste: plus vos fonctions sont atomiques, plus les chances de réutilisation du code sont élevées, sans avoir à effectuer le même changement à plusieurs endroits. , etc.

J'ai vu des procédures stockées SQL qui exécutent plus de 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, bordel. Non seulement vous devez continuer à faire défiler l'écran, vous devez également attribuer à quelques lignes de code un nom tel que "ceci ne se valide pas1", "ceci se met à jour dans la base de données", etc. - un travail que le programmeur aurait dû effectuer.


+1 pour le premier paragraphe seulement. tout le reste est relatif au cas sur lequel vous travaillez.
Nom d'affichage

Et en quoi le fractionnement en 50 fonctions est-il utile? Lorsque les fonctions doivent communiquer, vous ne finirez donc pas avec 500 lignes mais mille?
gnasher729

5

De la complexité cyclomatique (Wikipedia):

La complexité cyclomatique d'une section de code source est le nombre de chemins linéairement indépendants traversant le code source.

  • Je vous recommande de garder ce nombre inférieur à 10 dans une seule méthode. S'il arrive à 10, alors il est temps de re-factoriser.

  • Il existe des outils permettant d’évaluer votre code et de vous donner un numéro de complexité cyclomatique.

  • Vous devez vous efforcer d'intégrer ces outils dans votre pipeline de construction.

  • Ne poursuivez pas littéralement une méthode de taille, mais essayez de regarder sa complexité et ses responsabilités. S'il a plus d'une responsabilité, alors c'est probablement une bonne idée de re-factoriser. Si sa complexité cyclomatique augmente, alors il est probablement temps de re-factoriser.

  • Je suis à peu près certain qu'il existe d'autres outils qui vous donnent des retours similaires, mais je n'ai pas encore eu l'occasion de me renseigner à ce sujet.


Il a été démontré que la complexité cyclomatique, sur du code réel provenant d’un des grands référentiels publics, et non d’exemples artificiels, était très fortement corrélée au SLOC brut. Cela le rend fondamentalement inutile, car il est beaucoup plus facile de compter les retours chariots. (Oui, il est possible de jeu SLOC Soyez honnête, ici: Combien de temps quelqu'un qui GAMING les mesures de SLOC à votre employeur soit autorisé à continuer à dessiner un chèque de règlement.?)
John R. Strohm

4

En règle générale, j'essaie de garder mes méthodes / fonctions à ce qui convient à l'écran d'un moniteur 1680x1050. Si cela ne convient pas, utilisez des méthodes / fonctions d'assistance pour répartir la tâche.

Cela facilite la lisibilité à la fois sur l’écran et sur le papier.


Je fais la même chose, mais il est utile de spécifier le type et la taille de police que vous utilisez. Pour ma part, je préfère les "consolas" de la taille 14 suggérées par Scott hanselman. hanselman.com/blog/… Il est difficile de travailler pour la première fois avec une police aussi volumineuse, mais il est recommandé de toujours se rappeler que votre méthode doit être la plus petite possible.
Samuel le

4

Je ne mets pas de limite stricte à 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 final ne réduirait pas la simplicité. Je ne crois pas non plus que l'idée qu'une fonction ne doive faire 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 à l'heure actuelle, et l'extraction d'une partie de la fonction dans une nouvelle fonction ou classe pourrait résoudre le problème. Une fonction peut être trop longue 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 qui pourrait être utile face à des changements prévisibles.


Les fonctions font "une chose" à un niveau d'abstraction spécifique, et vous ne vous préoccupez que de ce niveau d'abstraction. Exactement. Si vous ne pouvez pas comprendre cela, alors je ne pense pas que vous compreniez l'abstraction.
Zoran Pavlovic

4

Assez court pour être optimisé correctement

Les méthodes doivent être assez courtes pour faire exactement une chose. La raison en est simple: votre code peut donc être correctement optimisé.

Dans un langage JIT-ted tel que Java ou C #, il est important que vos méthodes soient simples afin 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 fait a même été évoqué dans le document Effective C # de Bill Wagner .

Dans un langage de niveau inférieur, tel que C ou C ++, il est également important de disposer de méthodes courtes (une douzaine de lignes environ), car cela permet de réduire le besoin de stocker des variables locales dans la 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, l'utilisation de méthodes courtes facilite également l'optimisation du compilateur. Dans un langage dynamique, plus une fonctionnalité est "dynamique", plus il est difficile de l'optimiser. Par exemple, une méthode longue qui prend un X et peut renvoyer un Int, Float ou String s'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 quel type la fonction retournera, il pourra également optimiser le site d’appel de la fonction. (Par exemple, ne pas vérifier les conversions de type.)


Environ 99,999% des applications existantes contiennent beaucoup plus de choses qui ralentissent la vitesse des programmes tels que l’accès à la base de données, l’accès aux fichiers ou la latence du réseau. Réfléchir à la rapidité lors de la conception des méthodes peut constituer une raison valable pour les jeux, les applications en temps réel ou les rapports contenant des tonnes de données, mais pas dans d’autres cas. Cependant, c’est un bon point, mais aussi petit que je suis en tant que programmeur, je dois rarement faire ce type d’optimisation dans mes applications.
Samuel le

Vous faites ce genre de chose lorsque vous avez mesuré la vitesse de votre code et que vous l'avez trouvé trop lent, si vous savez ce que vous faites (ce n'est pas aussi simple que vous le pensez), et si vous le mesurez par la suite et que la vitesse s'est améliorée .
gnasher729

3

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 de toutes les options était une boucle unique. 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. Aucune affaire ne dépasse 3 lignes - une garde, 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.


2

La plupart des compilateurs ne se préoccupent pas de la longueur d'une fonction. Une fonction doit être fonctionnelle, mais facile à comprendre, à modifier et à réutiliser pour l’être humain. Choisissez la longueur qui vous convient le mieux.


1

Ma règle générale est qu'une fonction doit tenir à l'écran. J'ai trouvé que trois cas tendent à violer ceci:

1) Fonctions de répartition. Dans l'ancien temps, ils étaient courants, mais la plupart d'entre eux sont remplacés par un héritage d'objet ces jours-ci. Cependant, les objets ne fonctionnent que dans votre programme et vous verrez donc toujours des fonctions de répartition occasionnelles lorsque vous manipulerez des données provenant d'autres sources.

2) Fonctions qui accomplissent toute une série 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 # 2 mais où les étapes individuelles sont si petites qu’elles sont simplement alignées au lieu d’être appelées séparément.


1

Peut-être que la longueur de la fonction n'est pas une bonne métrique. Nous essayons d'utiliser la complexité cyclomatique , sur les méthodes également, et l'une des futures règles d'archivage du contrôle de source selon laquelle 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, ce qui est assez serré.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.