web-dev-qa-db-fra.com

Une fonction peut-elle être trop courte?

Chaque fois que je me retrouve à écrire la même logique plus d'une fois, je la colle généralement dans une fonction, donc il n'y a qu'un seul endroit dans mon application où je dois maintenir cette logique. Un effet secondaire est que je me retrouve parfois avec une ou deux fonctions de ligne telles que:

function conditionMet(){
   return x == condition;
}

OR

function runCallback(callback){
   if($.isFunction(callback))
     callback();
}

Est-ce paresseux ou une mauvaise pratique? Je demande seulement parce que cela se traduit par un plus grand nombre d'appels de fonction pour de très petits morceaux de logique.

128
Mark Brown

Hehe, oh Mr Brown, si seulement je pouvais persuader tous les développeurs que je rencontre de garder leurs fonctions aussi petites que cela, croyez-moi, le monde du logiciel serait un meilleur endroit!

1) La lisibilité de votre code est multipliée par dix.

2) Si facile à comprendre le processus de votre code en raison de la lisibilité.

3) DRY - Ne vous répétez pas - Vous vous y conformez très bien!

4) Testable. Les fonctions minuscules sont un million de fois plus faciles à tester que ces 200 méthodes de ligne que nous voyons trop souvent.

Oh et ne vous inquiétez pas du "saut de fonction" en termes de performances. Les versions "Release" et les optimisations du compilateur s'en occupent très bien pour nous, et les performances sont 99% du temps ailleurs dans la conception des systèmes.

Est-ce paresseux? - Bien au contraire!

Est-ce une mauvaise pratique? - Absolument pas. Mieux vaut tirer cette façon de faire des méthodes que les boules de goudron ou les "God Objects" qui sont tellement trop communs.

Continuez votre bon travail mon collègue artisan;)

166
Martin Blore

Je dirais qu'une méthode refactorisée est trop courte si:

  • Il duplique une opération primitive, dans aucun autre but que d'en faire une méthode:

Ex:

boolean isNotValue() {
   return !isValue();
}

ou...

  • Le code n'est utilisé qu'une seule fois et son intention est facile à comprendre en un coup d'œil.

Ex:

void showDialog() {
    Dialog singleUseDialog = new ModalDialog();
    configureDialog(singleUseDialog);
    singleUseDialog.show();
}

void configureDialog(Dialog singleUseDialog) {
    singleUseDialog.setDimensions(400, 300);
}

Cela pourrait être un modèle valide, mais je voudrais simplement intégrer la méthode configureDialog (), dans cet exemple, à moins que je n'ait l'intention de la remplacer ou de réutiliser ce code ailleurs.

64
RMorrisey

Une fonction peut-elle être trop courte? En général non.

En fait, la seule façon de s'assurer que:

  1. Vous avez trouvé toutes les classes dans votre conception
  2. Vos fonctions ne font qu'une seule chose.

Est de garder vos fonctions aussi petites que possible. Ou, en d'autres termes, extrayez des fonctions de vos fonctions jusqu'à ce que vous ne puissiez plus en extraire. J'appelle cela "Extraire jusqu'à ce que vous tombiez".

Pour expliquer cela: Une fonction est une portée avec des morceaux de fonctionnalité qui communiquent par variables. Une classe est également une portée avec des blocs de fonctionnalités qui communiquent par variables. Ainsi, une fonction longue peut toujours être remplacée par une ou plusieurs classes avec une petite méthode.

De plus, une fonction suffisamment grande pour vous permettre d'en extraire une autre, fait plus d'une chose par définition. Donc, si vous pouvez extraire une fonction d'une autre, vous devriez extraire cette fonction.

Certains craignent que cela n'entraîne une prolifération des fonctions. Ils ont raison. Ce sera. C'est en fait une bonne chose. C'est bien parce que les fonctions ont des noms. Si vous faites attention à choisir de bons noms, ces fonctions agissent comme des panneaux de signalisation qui dirigent d'autres personnes à travers votre code. En effet, des fonctions bien nommées à l'intérieur de classes bien nommées à l'intérieur d'espaces de noms bien nommés sont l'une des meilleures façons de s'assurer que vos lecteurs ne se perdent pas.

Il y a beaucoup plus à ce sujet dans l'épisode III de Clean Code sur cleancoders.com

59
Uncle Bob.

Wow, la plupart de ces réponses ne sont pas très utiles du tout.

Aucune fonction ne doit être écrite dont l'identité est sa définition . Autrement dit, si le nom de la fonction est simplement le bloc de code de la fonction écrit en anglais, alors ne l'écrivez pas en tant que fonction.

Considérez votre fonction conditionMet et cette autre fonction, addOne (pardonnez-moi mon JavaScript rouillé):

function conditionMet() { return x == condition; }

function addOne(x) { return x + 1; }

conditionMet est une définition conceptuelle appropriée; addOne est un tautologie . conditionMet est bon parce que vous ne savez pas ce que conditionMet implique simplement en disant "condition remplie", mais vous pouvez voir pourquoi addOne est idiot si vous le lisez en anglais :

"For the condition to be met is for x to equal condition" <-- explanatory! meaningful! useful!

"To add one to x is to take x and add one." <-- wtf!

Pour l'amour de tout ce qui pourrait encore être saint, n'écrivez pas de fonctions tautologiques!

(Et pour la même raison, n'écrivez pas de commentaires pour chaque ligne de code !)

53
Rei Miyasaka

Je dirais que si vous pensez que l'intention d'un code peut être améliorée en ajoutant un commentaire, puis plutôt que d'ajouter ce commentaire, extrayez le code dans sa propre méthode. Peu importe la taille du code.

Ainsi, par exemple, si votre code devait ressembler à:

if x == 1 { ... } // is pixel on?

faites-le ressembler à ceci à la place:

if pixelOn() { ... }

avec

function pixelOn() { return x == 1; }

Ou en d'autres termes, il ne s'agit pas de la longueur de la méthode, mais du code auto-documenté.

14
Julio

Je pense que c'est exactement ce que vous voulez faire. À l'heure actuelle, cette fonction peut ne comporter qu'une ou deux lignes, mais au fil du temps, elle pourrait augmenter. Avoir également plus d'appels de fonction vous permet de lire les appels de fonction et de comprendre ce qui se passe à l'intérieur. Cela rend votre code très DRY (Don't Repeat Yourself) qui est beaucoup plus maintenable.

7
mpenrow

Je suis d'accord avec tous les autres messages que j'ai vus. C'est du bon style.

Les frais généraux d'une méthode aussi petite peuvent être nuls car l'optimiseur peut optimiser l'appel et éloigner le code. Un code simple comme celui-ci permet à l'optimiseur de faire de son mieux.

Le code doit être écrit pour plus de clarté et de simplicité. J'essaie de limiter une méthode à l'un des deux rôles: prendre des décisions; ou effectuer un travail. Cela peut générer des méthodes d'une ligne. Mieux je fais ça, mieux je trouve mon code.

Un code comme celui-ci a généralement une forte cohésion et un faible couplage, ce qui est une bonne pratique de codage.

EDIT: une note sur les noms des méthodes. Utilisez un nom de méthode qui indique ce que la méthode ne fait pas comment elle le fait. Je trouve que verb_noun (_modifier) ​​est un bon schéma de nommage. Cela donne des noms comme Find_Customer_ByName plutôt que Select_Customer_Using_NameIdx. Le deuxième cas est susceptible de devenir incorrect lorsque la méthode est modifiée. Dans le premier cas, vous pouvez échanger l'intégralité de l'implémentation de la base de données client.

5
BillThor

Refactoriser une ligne de code dans une fonction semble excessif. Il peut y avoir des cas exceptionnels, tels que des lignes ou des exporations de ver loooooong/comples, mais je ne le ferais pas à moins que je sais la fonction se développe à l'avenir.

et votre premier exemple fait allusion à l'utilisation des globaux (qui peuvent ou non parler d'autres problèmes dans le code), je le remodelerais plus loin et ferais ces deux variables comme paramètres:

function conditionMet(x, condition){
   return x == condition;
}
....
conditionMet(1,(3-2));
conditionMet("abc","abc");

L'exemple conditionMetpourrait être utile si la condition était longue et répétitive telle que:

function conditionMet(x, someObject){
   return x == ((someObject.valA + someObject.valB - 15.4) / /*...whole bunch of other stuff...*/);
}

Considère ceci:

Une fonction de détection de collision simple:

bool collide(OBJ a, OBJ b)
{
    return(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) <= pow(a.radius + b.radius, 2));
}

Si vous écrivez cette "simple" doublure dans votre code tout le temps, vous pourriez éventuellement faire une erreur. De plus, ce serait vraiment tortueux d'écrire ça encore et encore.

3
Mateen Ulhaq

Je dirais qu'ils sont trop courts, mais c'est mon opinion subjective.

Parce que:

  • Il n'y a aucune raison de créer une fonction si elle n'est utilisée qu'une ou deux fois. Sauter aux défs est nul. Surtout avec du code VS et C++ incroyablement rapide.
  • Aperçu des cours. Lorsque vous avez des milliers de petites fonctions, cela me met en colère. J'apprécie quand je peux afficher les définitions de classe et voir rapidement ce qu'il fait, pas comment il SetXToOne, SetYToVector3, MultiplyNumbers, + 100 setters/getters.
  • Dans la plupart des projets, ces aides deviennent un poids mort après une ou deux phases de refactoring, puis vous "recherchez tout" -> supprimer pour vous débarrasser du code obsolète, généralement ~ 25% +.

Les fonctions qui sont longues sont mauvaises, mais les fonctions qui sont plus courtes que 3 lignes et exécutent seulement 1 chose sont également mauvaises IMHO.

Je dirais donc seulement écrire une petite fonction si c'est:

  • 3+ lignes de code
  • Est-ce que les développeurs juniors pourraient manquer (je ne sais pas)
  • Fait une validation supplémentaire
  • Est utilisé ou utilisera au moins 3 fois
  • Simplifie l'interface fréquemment utilisée
  • Ne deviendra pas un poids mort lors de la prochaine refactorisation
  • A une signification particulière, par exemple, spécialisation de modèle ou quelque chose
  • Effectue un travail d'isolement - références const, affecte les paramètres modifiables, effectue la récupération des membres privés

Je parie que le prochain développeur (senior) aura mieux à faire que de se souvenir de toutes vos fonctions SetXToOne. Donc, ils se transformeront bientôt en poids mort de toute façon.

2
Coder

Non, et c'est rarement un problème. Maintenant, si quelqu'un estime qu'aucune fonction ne devrait être plus longue qu'une ligne de code (si seulement cela pouvait être aussi simple), ce serait un problème et à certains égards paresseux parce qu'il ne pense pas à ce qui est approprié.

2
JeffO

Je n'aime pas l'exemple non. 1, à cause du ième nom générique.

conditionMet ne semble pas être générique, donc il représente une condition spécifique? Comme

isAdult () = { 
  age >= 18 
}

Ce serait bien. C'est une différence sémantique, alors que

isAtLeast18 () { age >= 18; } 

ne serait pas bien pour moi.

Peut-être qu'il est souvent utilisé et peut faire l'objet de modifications ultérieures:

getPreferredIcecream () { return List ("banana", "choclate", "Vanilla", "walnut") }

c'est bien aussi. Si vous l'utilisez plusieurs fois, il vous suffit de changer un seul endroit, si vous le devez - peut-être que la crème fouettée sera possible demain.

isXYZ (Foo foo) { foo.x > 15 && foo.y < foo.x * 2 }

n'est pas atomique, et devrait vous donner une belle opportunité de test.

Si vous avez besoin de passer une fonction, bien sûr, passez ce que vous voulez et écrivez des fonctions qui paraissent stupides.

Mais en général, je vois beaucoup plus de fonctions, qui sont trop longues, que de fonctions qui sont trop courtes.

Un dernier mot: Certaines fonctions ne semblent appropriées que parce qu'elles sont écrites trop verbeuses:

function lessThan (a, b) {
  if (a < b) return true else return false; 
}

Si vous voyez, c'est la même chose que

return (a < b); 

vous n'aurez pas de problème avec

localLessThan = (a < b); 

au lieu de

localLessThan = lessThan (a, b); 
1
user unknown

Il n'y a pas de code comme pas de code!

Restez simple et ne compliquez pas trop les choses.

Ce n'est pas être paresseux, c'est faire votre travail!

0
RDL

Trop court n'est jamais un problème. Certaines raisons pour les fonctions courtes sont:

Réutilisation

Par exemple. si vous avez une fonction, comme une méthode set, vous pouvez l'affirmer pour vous assurer que les paramètres sont valides. Cette vérification doit être effectuée partout où la variable est définie.

maintenabilité

Vous pourriez utiliser une déclaration qui, selon vous, pourrait changer à l'avenir. Par exemple. vous affichez maintenant un symbole dans une colonne, mais plus tard, cela pourrait changer en quelque chose d'autre (ou même un bip).

niformité

Vous utilisez par exemple le motif de façade et la seule chose qu'une fonction fait est de passer exactement la fonction à une autre sans changer d'argument.

0
Michel Keijzers

Lorsque vous donnez un nom à une section de code, c'est essentiellement pour lui donner un nom. Cela peut être pour plusieurs raisons, mais le point crucial est de savoir si vous pouvez lui donner un nom significatif qui s'ajoute à votre programme. Des noms comme "addOneToCounter" n'ajouteraient rien, mais conditionMet() le ferait.

Si vous avez besoin d'une règle pour savoir si l'extrait est trop court ou trop long, réfléchissez au temps qu'il vous faut pour trouver un nom significatif pour l'extrait. Si vous ne pouvez pas le faire dans un délai raisonnable, l'extrait de code n'est pas de taille appropriée.

0
user1249

Oui, c'est ok d'avoir une fonction de code court. Dans le cas de méthodes telles que les "getters", les "setters", les "accesors" est très courant, comme l'ont mentionné les réponses précédentes.

Parfois, ces courtes fonctions "accesors" sont virtuelles, car lorsqu'elles sont remplacées dans les sous-classes, les fonctions auront plus de code.

Si vous voulez que votre fonction ne soit pas aussi courte, eh bien, dans de nombreuses fonctions, plus globales ou méthodes, j'utilise généralement une variable "résultat" (style Pascal) au lieu d'un retour direct, c'est très utile lorsque vous utilisez un débogueur.

function int CalculateSomething() {
  int Result = -1;

   // more code, maybe, maybe not

  return Result;
}
0
umlcat

Non, mais cela peut être trop laconique.

N'oubliez pas: le code est écrit une fois, mais lu plusieurs fois.

N'écrivez pas de code pour le compilateur. Écrivez-le pour les futurs développeurs qui devront maintenir votre code.

0
Jim G.