Y a-t-il de bonnes raisons pour lesquelles il est préférable d'avoir une seule instruction de retour dans une fonction?
Ou est-il correct de revenir d'une fonction dès qu'il est logiquement correct de le faire, ce qui signifie qu'il peut y avoir de nombreuses instructions de retour dans la fonction?
language-agnostic
coding-style
Tim Post
la source
la source
Réponses:
J'ai souvent plusieurs instructions au début d'une méthode pour revenir à des situations "faciles". Par exemple, ceci:
... peut être rendu plus lisible (à mon humble avis) comme ceci:
Alors oui, je pense que c'est bien d'avoir plusieurs "points de sortie" d'une fonction / méthode.
la source
DoStuff() { DoStuffInner(); IncreaseStuffCallCounter(); }
Personne n'a mentionné ou cité Code Complete, alors je vais le faire.
17.1 retour
Minimisez le nombre de retours dans chaque routine . Il est plus difficile de comprendre une routine si, en la lisant en bas, vous n'êtes pas conscient de la possibilité qu'elle soit revenue quelque part au-dessus.
Utilisez un retour lorsqu'il améliore la lisibilité . Dans certaines routines, une fois que vous connaissez la réponse, vous souhaitez la retourner immédiatement à la routine d'appel. Si la routine est définie de telle manière qu'elle ne nécessite aucun nettoyage, ne pas retourner immédiatement signifie que vous devez écrire plus de code.
la source
Je dirais qu'il serait incroyablement imprudent de décider arbitrairement contre plusieurs points de sortie car j'ai trouvé la technique utile dans la pratique à maintes reprises , en fait, j'ai souvent refactorisé le code existant en plusieurs points de sortie pour plus de clarté. On peut ainsi comparer les deux approches: -
Comparez cela au code où plusieurs points de sortie sont autorisés: -
Je pense que ce dernier est beaucoup plus clair. Autant que je sache, la critique de plusieurs points de sortie est un point de vue plutôt archaïque de nos jours.
la source
Je travaille actuellement sur une base de code où deux des personnes qui y travaillent souscrivent aveuglément à la théorie du «point de sortie unique» et je peux vous dire que par expérience, c'est une horrible pratique horrible. Cela rend le code extrêmement difficile à maintenir et je vais vous montrer pourquoi.
Avec la théorie du «point de sortie unique», vous vous retrouvez inévitablement avec du code qui ressemble à ceci:
Non seulement cela rend le code très difficile à suivre, mais dites maintenant plus tard que vous devez revenir en arrière et ajouter une opération entre 1 et 2. Vous devez mettre en retrait à peu près toute la fonction de panique, et bonne chance pour vous assurer que tout vos conditions et accolades if / else sont correctement mises en correspondance.
Cette méthode rend la maintenance du code extrêmement difficile et sujette aux erreurs.
la source
La programmation structurée indique que vous ne devriez avoir qu'une seule instruction de retour par fonction. C'est pour limiter la complexité. Beaucoup de gens comme Martin Fowler soutiennent qu'il est plus simple d'écrire des fonctions avec plusieurs déclarations de retour. Il présente cet argument dans le livre de refactorisation classique qu'il a écrit. Cela fonctionne bien si vous suivez ses autres conseils et écrivez de petites fonctions. Je suis d'accord avec ce point de vue et seuls les puristes de la programmation structurée stricte adhèrent à des déclarations de retour uniques par fonction.
la source
GOTO
pour déplacer le flux de contrôle même lorsque la fonction existe. Il ne dit jamais "ne jamais utiliserGOTO
".Comme Kent Beck le note lors de l'examen des clauses de garde dans les modèles d'implémentation, la création d'une routine a un point d'entrée et de sortie unique ...
Je trouve une fonction écrite avec des clauses de garde beaucoup plus facile à suivre qu'un long groupe d'
if then else
instructions imbriquées .la source
Dans une fonction qui n'a pas d'effets secondaires, il n'y a aucune bonne raison d'avoir plus d'un seul retour et vous devez les écrire dans un style fonctionnel. Dans une méthode avec des effets secondaires, les choses sont plus séquentielles (indexées dans le temps), vous écrivez donc dans un style impératif, en utilisant l'instruction return comme commande pour arrêter l'exécution.
En d'autres termes, si possible, privilégiez ce style
sur ceci
Si vous vous retrouvez en train d'écrire plusieurs couches de conditions imbriquées, il existe probablement un moyen de refactoriser cela, en utilisant la liste de prédicats par exemple. Si vous trouvez que vos ifs et elses sont très éloignés syntaxiquement, vous voudrez peut-être les décomposer en fonctions plus petites. Un bloc conditionnel qui s'étend sur plus d'un écran de texte est difficile à lire.
Il n'y a pas de règle stricte qui s'applique à toutes les langues. Quelque chose comme avoir une seule déclaration de retour ne rendra pas votre code bon. Mais un bon code aura tendance à vous permettre d'écrire vos fonctions de cette façon.
la source
Je l'ai vu dans les normes de codage pour C ++ qui étaient un blocage de C, comme si vous n'avez pas RAII ou une autre gestion automatique de la mémoire, vous devez nettoyer à chaque retour, ce qui signifie soit couper-coller du nettoyage ou d'un goto (logiquement le même que «finalement» dans les langages gérés), les deux étant considérés comme de mauvaise forme. Si vos pratiques consistent à utiliser des pointeurs intelligents et des collections en C ++ ou un autre système de mémoire automatique, il n'y a pas de raison valable pour cela, et tout devient question de lisibilité, et plus d'un appel de jugement.
la source
auto_ptr
, vous pouvez utiliser des pointeurs simples en parallèle. Bien qu'il serait étrange d'écrire du code «optimisé» avec un compilateur non optimisant en premier lieu.try
...finally
en Java) et que vous devez faire la maintenance des ressources, vous pouvez le faire avec un seul retour à la fin d'une méthode. Avant de faire cela, vous devriez sérieusement envisager de refactoriser le code pour vous débarrasser de la situation.Je penche pour l'idée que les instructions de retour au milieu de la fonction sont mauvaises. Vous pouvez utiliser des retours pour construire quelques clauses de garde en haut de la fonction, et bien sûr dire au compilateur quoi retourner à la fin de la fonction sans problème, mais les retours au milieu de la fonction peuvent être facilement manqués et peuvent rendre la fonction plus difficile à interpréter.
la source
Oui , il y a:
La question est souvent posée comme une fausse dichotomie entre plusieurs déclarations ou des déclarations si profondément imbriquées. Il y a presque toujours une troisième solution qui est très linéaire (pas d'imbrication profonde) avec un seul point de sortie.
Mise à jour : les directives MISRA semblent également promouvoir la sortie unique .
Pour être clair, je ne dis pas qu'il est toujours faux d'avoir plusieurs déclarations. Mais étant donné des solutions par ailleurs équivalentes, il existe de nombreuses bonnes raisons de préférer celle avec un seul retour.
la source
Contract.Ensures
avec plusieurs points de retour.goto
pour accéder au code de nettoyage commun, vous avez probablement simplifié la fonction afin qu'il y en ait unreturn
à la fin du code de nettoyage. Vous pourriez donc dire que vous avez résolu le problème avecgoto
, mais je dirais que vous l'avez résolu en simplifiant à un seulreturn
.Avoir un seul point de sortie offre un avantage dans le débogage, car il vous permet de définir un seul point d'arrêt à la fin d'une fonction pour voir quelle valeur va réellement être renvoyée.
la source
En général, j'essaie de n'avoir qu'un seul point de sortie d'une fonction. Il arrive cependant que cela finisse par créer un corps de fonction plus complexe que nécessaire, auquel cas il est préférable d'avoir plusieurs points de sortie. Il doit vraiment s'agir d'un "appel au jugement" basé sur la complexité qui en résulte, mais l'objectif doit être le moins de points de sortie possible sans sacrifier la complexité et la compréhensibilité.
la source
Non, parce que nous ne vivons plus dans les années 1970 . Si votre fonction est suffisamment longue pour que plusieurs retours soient un problème, elle est trop longue.
(Indépendamment du fait que toute fonction multiligne dans une langue avec des exceptions aura de toute façon plusieurs points de sortie.)
la source
Ma préférence serait pour une sortie unique à moins que cela ne complique vraiment les choses. J'ai constaté que dans certains cas, plusieurs points existants peuvent masquer d'autres problèmes de conception plus importants:
En voyant ce code, je demanderais immédiatement:
Selon les réponses à ces questions, il se pourrait que
Dans les deux cas ci-dessus, le code peut probablement être retravaillé avec une assertion pour s'assurer que «foo» n'est jamais nul et que les appelants concernés ont changé.
Il y a deux autres raisons (spécifiques je pense au code C ++) où plusieurs existent peuvent avoir un effet négatif . Ce sont la taille du code et les optimisations du compilateur.
Un objet non POD C ++ dans la portée à la sortie d'une fonction aura son destructeur appelé. Lorsqu'il y a plusieurs instructions de retour, il se peut qu'il y ait différents objets dans la portée et donc la liste des destructeurs à appeler sera différente. Le compilateur doit donc générer du code pour chaque instruction de retour:
Si la taille du code est un problème - cela peut être quelque chose à éviter.
L'autre problème concerne "l'optimisation de la valeur de retour nommée" (alias Copy Elision, ISO C ++ '03 12.8 / 15). C ++ permet à une implémentation d'ignorer l'appel du constructeur de copie s'il peut:
En prenant simplement le code tel quel, l'objet 'a1' est construit dans 'foo' et ensuite sa construction de copie sera appelée pour construire 'a2'. Cependant, la copie élision permet au compilateur de construire «a1» au même endroit sur la pile que «a2». Il n'est donc pas nécessaire de "copier" l'objet lorsque la fonction revient.
Plusieurs points de sortie compliquent le travail du compilateur pour essayer de détecter cela, et au moins pour une version relativement récente de VC ++, l'optimisation n'a pas eu lieu lorsque le corps de la fonction avait plusieurs retours. Voir Optimisation de la valeur de retour nommée dans Visual C ++ 2005 pour plus de détails.
la source
throw new ArgumentNullException()
en C # dans ce cas), j'ai vraiment aimé vos autres considérations, elles sont toutes valables pour moi et pourraient être critiques dans certains contextes de niche.foo
est testé n'a rien à voir avec le sujet, qui est de savoir s'il faut le faireif (foo == NULL) return; dowork;
ouif (foo != NULL) { dowork; }
Avoir un seul point de sortie réduit la complexité cyclomatique et donc, en théorie , réduit la probabilité que vous introduisiez des bogues dans votre code lorsque vous le modifiez. Cependant, la pratique tend à suggérer qu'une approche plus pragmatique est nécessaire. J'ai donc tendance à viser à avoir un seul point de sortie, mais autoriser mon code à en avoir plusieurs si c'est plus lisible.
la source
Je m'oblige à n'utiliser qu'une seule
return
instruction, car cela va en quelque sorte générer une odeur de code. Laisse-moi expliquer:(Les conditions sont arbritaires ...)
Plus il y a de conditions, plus la fonction est grande, plus elle est difficile à lire. Donc, si vous êtes à l'écoute de l'odeur du code, vous vous en rendrez compte et voudrez refactoriser le code. Deux solutions possibles sont:
Retours multiples
Fonctions séparées
Certes, c'est plus long et un peu désordonné, mais dans le processus de refactorisation de la fonction de cette façon, nous avons
la source
Je dirais que vous devriez en avoir autant que nécessaire, ou tout ce qui rend le code plus propre (comme les clauses de garde ).
Personnellement, je n'ai jamais entendu / vu de "meilleures pratiques" disant que vous ne devriez avoir qu'une seule déclaration de retour.
Pour la plupart, j'ai tendance à quitter une fonction dès que possible sur la base d'un chemin logique (les clauses de garde en sont un excellent exemple).
la source
Je crois que les retours multiples sont généralement bons (dans le code que j'écris en C #). Le style à retour unique est un maintien de C. Mais vous ne codez probablement pas en C.
Il n'y a pas de loi exigeant un seul point de sortie pour une méthode dans tous les langages de programmation . Certaines personnes insistent sur la supériorité de ce style, et parfois elles l'élèvent à une «règle» ou «loi», mais cette croyance n'est étayée par aucune preuve ou recherche.
Plus d'un style de retour peut être une mauvaise habitude dans le code C, où les ressources doivent être explicitement désallouées, mais des langages tels que Java, C #, Python ou JavaScript qui ont des constructions telles que le garbage collection automatique et les
try..finally
blocs (et lesusing
blocs en C # ), et cet argument ne s'applique pas - dans ces langues, il est très rare d'avoir besoin d'une désallocation manuelle centralisée des ressources.Il y a des cas où un seul retour est plus lisible et des cas où il ne l'est pas. Voyez si cela réduit le nombre de lignes de code, rend la logique plus claire ou réduit le nombre d'accolades et de retraits ou de variables temporaires.
Par conséquent, utilisez autant de retours que vous le souhaitez, car il s'agit d'un problème de mise en page et de lisibilité, pas technique.
J'en ai parlé plus longuement sur mon blog .
la source
Il y a de bonnes choses à dire sur le fait d'avoir un seul point de sortie, tout comme il y a de mauvaises choses à dire sur l'inévitable programmation "en flèche" qui en résulte.
Si j'utilise plusieurs points de sortie lors de la validation des entrées ou de l'allocation des ressources, j'essaie de mettre très visiblement toutes les «sorties d'erreur» en haut de la fonction.
L' article sur la programmation spartiate de "SSDSLPedia" et l' article sur le point de sortie de fonction unique du "Wiki de Portland Pattern Repository" contiennent des arguments perspicaces à ce sujet. Bien sûr, il y a aussi ce post à considérer.
Si vous voulez vraiment un seul point de sortie (dans n'importe quelle langue sans exception) par exemple afin de libérer des ressources en un seul endroit, je trouve que l'application soignée de goto est bonne; voir par exemple cet exemple plutôt artificiel (compressé pour économiser l'écran immobilier):
Personnellement, en général, je n'aime pas plus la programmation des flèches que je n'aime plusieurs points de sortie, bien que les deux soient utiles lorsqu'ils sont appliqués correctement. Le mieux, bien sûr, est de structurer votre programme pour ne demander ni l'un ni l'autre. Décomposer votre fonction en plusieurs morceaux aide généralement :)
Bien que ce faisant, je trouve que je me retrouve de toute façon avec plusieurs points de sortie comme dans cet exemple, où une fonction plus grande a été décomposée en plusieurs fonctions plus petites:
Selon le projet ou les directives de codage, la plupart du code de la plaque de la chaudière pourrait être remplacé par des macros. En remarque, le décomposer de cette façon rend les fonctions g0, g1, g2 très faciles à tester individuellement.
De toute évidence, dans un langage OO et activé par exception, je n'utiliserais pas des instructions if comme ça (ou pas du tout, si je pouvais m'en tirer avec assez peu d'effort), et le code serait beaucoup plus simple. Et non fléché. Et la plupart des retours non définitifs seraient probablement des exceptions.
En bref;
la source
Vous connaissez l'adage - la beauté est aux yeux du spectateur .
Certaines personnes ne jurent que par NetBeans et certaines par IntelliJ IDEA , certaines par Python et d'autres par PHP .
Dans certains magasins, vous pourriez perdre votre emploi si vous insistez pour le faire:
La question concerne la visibilité et la maintenabilité.
Je suis accro à l'utilisation de l'algèbre booléenne pour réduire et simplifier la logique et l'utilisation des machines à états. Cependant, certains collègues ont estimé que mon emploi des "techniques mathématiques" dans le codage n'était pas approprié, car il ne serait ni visible ni maintenable. Et ce serait une mauvaise pratique. Désolé les gens, les techniques que j'emploie sont très visibles et maintenables pour moi - parce que lorsque je reviens au code six mois plus tard, je comprendrais clairement le code plutôt que de voir un gâchis de spaghettis proverbiaux.
Hé mon pote (comme disait un ancien client) fais ce que tu veux tant que tu sais comment le réparer quand j'ai besoin de toi pour le réparer.
Je me souviens il y a 20 ans, un de mes collègues a été licencié pour avoir utilisé ce que l'on appelle aujourd'hui une stratégie de développement agile . Il avait un plan progressif méticuleux. Mais son manager lui criait: «Vous ne pouvez pas libérer progressivement les fonctionnalités pour les utilisateurs! Vous devez vous en tenir à la cascade ." Sa réponse au directeur était que le développement progressif serait plus précis aux besoins du client. Il croyait au développement pour les besoins des clients, mais le directeur croyait au codage selon «l'exigence du client».
Nous sommes fréquemment coupables d'avoir enfreint la normalisation des données, les limites MVP et MVC . Nous inline au lieu de construire une fonction. Nous prenons des raccourcis.
Personnellement, je pense que PHP est une mauvaise pratique, mais que sais-je? Tous les arguments théoriques se résument à essayer de respecter un ensemble de règles
Toutes les autres règles disparaissent en arrière-plan. Et bien sûr, cette règle ne s'estompe jamais:
la source
Je penche pour l'utilisation de clauses de garde pour revenir tôt et sinon quitter à la fin d'une méthode. La règle d'entrée et de sortie unique a une signification historique et était particulièrement utile lorsqu'il s'agissait de code hérité qui comptait 10 pages A4 pour une seule méthode C ++ avec plusieurs retours (et de nombreux défauts). Plus récemment, la bonne pratique acceptée consiste à conserver des méthodes réduites, ce qui rend les sorties multiples moins gênantes pour la compréhension. Dans l'exemple Kronoz suivant copié ci-dessus, la question est de savoir ce qui se passe dans // Reste de code ... ?:
Je me rends compte que l'exemple est quelque peu artificiel, mais je serais tenté de refactoriser le boucle foreach dans une instruction LINQ qui pourrait alors être considérée comme une clause de garde. Encore une fois, dans un exemple artificiel, l'intention du code n'est pas apparente et someFunction () peut avoir un autre effet secondaire ou le résultat peut être utilisé dans le // reste du code ... .
Donner la fonction refactorisée suivante:
la source
null
au lieu de lever une exception indiquant que l'argument n'est pas accepté?Une bonne raison à laquelle je peux penser est pour la maintenance du code: vous avez un seul point de sortie. Si vous voulez changer le format du résultat, ..., c'est juste beaucoup plus simple à implémenter. De plus, pour le débogage, vous pouvez simplement y coller un point d'arrêt :)
Cela dit, j'ai dû une fois travailler dans une bibliothèque où les normes de codage imposaient «une déclaration de retour par fonction», et j'ai trouvé cela assez difficile. J'écris beaucoup de code de calcul numérique, et il y a souvent des 'cas spéciaux', donc le code a fini par être assez difficile à suivre ...
la source
Plusieurs points de sortie conviennent pour des fonctions suffisamment petites - c'est-à-dire une fonction qui peut être visualisée sur une seule longueur d'écran sur son intégralité. Si une fonction longue comprend également plusieurs points de sortie, c'est un signe que la fonction peut être hachée davantage.
Cela dit, j'évite les fonctions à sorties multiples, sauf si cela est absolument nécessaire . J'ai ressenti la douleur de bugs qui sont dus à un retour errant dans une ligne obscure dans des fonctions plus complexes.
la source
J'ai travaillé avec des normes de codage terribles qui vous ont imposé un seul chemin de sortie et le résultat est presque toujours des spaghettis non structurés si la fonction est tout sauf triviale - vous vous retrouvez avec beaucoup de pauses et continue qui ne fait qu'empêcher.
la source
if
déclaration devant chaque appel de méthode qui renvoie le succès ou non :(Un point de sortie unique - toutes choses égales par ailleurs - rend le code beaucoup plus lisible. Mais il y a un hic: la construction populaire
est un faux, "res =" n'est pas beaucoup mieux que "return". Il a une déclaration de retour unique, mais plusieurs points où la fonction se termine réellement.
Si vous avez une fonction avec plusieurs retours (ou "res =" s), c'est souvent une bonne idée de la diviser en plusieurs fonctions plus petites avec un seul point de sortie.
la source
Ma politique habituelle est de n'avoir qu'une seule instruction de retour à la fin d'une fonction, à moins que la complexité du code ne soit considérablement réduite en ajoutant plus. En fait, je suis plutôt un fan d'Eiffel, qui applique la seule règle de retour en n'ayant pas de déclaration de retour (il y a juste une variable 'result' créée automatiquement pour mettre votre résultat).
Il y a certainement des cas où le code peut être rendu plus clair avec plusieurs retours que la version évidente sans eux. On pourrait dire que plus de retouches sont nécessaires si vous avez une fonction trop complexe pour être compréhensible sans plusieurs déclarations de retour, mais parfois il est bon d'être pragmatique à propos de telles choses.
la source
Si vous vous retrouvez avec plus de quelques retours, il peut y avoir un problème avec votre code. Sinon, je conviens que parfois, il est agréable de pouvoir revenir de plusieurs endroits dans un sous-programme, surtout quand il rend le code plus propre.
Perl 6: mauvais exemple
serait mieux écrit comme ça
Perl 6: bon exemple
Notez que ce n'était qu'un exemple rapide
la source
Je vote pour le retour unique à la fin comme ligne directrice. Cela permet une gestion courante du nettoyage du code ... Par exemple, jetez un œil au code suivant ...
la source
C'est probablement une perspective inhabituelle, mais je pense que quiconque croit que plusieurs déclarations de retour sont à privilégier n'a jamais eu à utiliser un débogueur sur un microprocesseur qui ne prend en charge que 4 points d'arrêt matériels. ;-)
Bien que les problèmes de "code de flèche" soient complètement corrects, un problème qui semble disparaître lors de l'utilisation de plusieurs instructions de retour se trouve dans la situation où vous utilisez un débogueur. Vous n'avez pas de position fourre-tout pratique pour mettre un point d'arrêt pour garantir que vous allez voir la sortie et donc la condition de retour.
la source
Plus vous avez d'instructions de retour dans une fonction, plus la complexité de cette méthode est élevée. Si vous vous demandez si vous avez trop de déclarations de retour, vous voudrez peut-être vous demander si vous avez trop de lignes de code dans cette fonction.
Mais, non, il n'y a rien de mal avec une / plusieurs déclarations de retour. Dans certains langages, c'est une meilleure pratique (C ++) que dans d'autres (C).
la source