D'où vient la notion de «un seul retour»?

1055

Je parle souvent aux programmeurs qui disent " Ne mettez pas plusieurs déclarations avec la même méthode. " Quand je leur demande de me dire pourquoi, tout ce que je reçois est " La norme de codage le dit. " Ou " C'est déroutant. " Quand ils me montrent des solutions avec une seule déclaration de retour, le code me parait plus laid. Par exemple:

if (condition)
   return 42;
else
   return 97;

" C'est moche, il faut utiliser une variable locale! "

int result;
if (condition)
   result = 42;
else
   result = 97;
return result;

En quoi ce problème de code à 50% facilite-t-il la compréhension du programme? Personnellement, je trouve cela plus difficile, car l’espace d’état vient d’être augmenté d’une autre variable qui aurait pu être facilement évitée.

Bien sûr, normalement j'écrirais simplement:

return (condition) ? 42 : 97;

Mais de nombreux programmeurs évitent l’opérateur conditionnel et préfèrent la forme longue.

D'où vient cette notion de "un seul retour"? Y at-il une raison historique à l’origine de cette convention?

fredoverflow
la source
2
Ceci est en quelque sorte lié au refactoring de Guard Clause. stackoverflow.com/a/8493256/679340 Guard Clause ajoutera des retours au début de vos méthodes. Et cela rend le code beaucoup plus propre à mon avis.
Piotr Perak
3
Cela venait de la notion de programmation structurée. Certains diront peut-être qu'avoir un seul retour vous permet de modifier facilement le code pour qu'il fasse quelque chose juste avant le retour ou pour déboguer facilement.
martinkunev
3
Je pense que l'exemple est un cas assez simple où je n'aurais pas une opinion forte d'une manière ou d'une autre. L’idéal d’une entrée unique à une sortie est plutôt un moyen de nous éloigner de situations folles telles que 15 déclarations de retour et deux autres branches qui ne reviennent pas du tout!
Mendota
2
C'est l'un des pires articles que j'ai jamais lu. Il semble que l’auteur passe plus de temps à fantasmer sur la pureté de sa POO qu’à déterminer comment réaliser quoi que ce soit. Les arbres d'expression et d'évaluation ont une valeur, mais pas lorsque vous pouvez simplement écrire une fonction normale à la place.
DeadMG
3
Vous devriez supprimer la condition complètement. La réponse est 42.
cambunctious

Réponses:

1120

"Single Entry, Single Exit" était écrit lorsque la plupart de la programmation était faite en langage assembleur, en FORTRAN ou en COBOL. Il a été largement mal interprété, car les langues modernes ne supportent pas les pratiques mises en garde par Dijkstra.

"Entrée simple" signifiait "ne créez pas de points d'entrée alternatifs pour les fonctions". En langage assembleur, bien entendu, il est possible de saisir une fonction pour n’importe quelle instruction. FORTRAN prenait en charge plusieurs entrées de fonctions avec l’ ENTRYinstruction suivante:

      SUBROUTINE S(X, Y)
      R = SQRT(X*X + Y*Y)
C ALTERNATE ENTRY USED WHEN R IS ALREADY KNOWN
      ENTRY S2(R)
      ...
      RETURN
      END

C USAGE
      CALL S(3,4)
C ALTERNATE USAGE
      CALL S2(5)

"Single Exit" signifie qu'une fonction ne doit retourner qu'à un seul endroit: l'instruction qui suit immédiatement l'appel. Cela ne signifiait pas qu'une fonction ne devrait revenir que d' un seul endroit. Lors de l' écriture de la programmation structurée , il était courant qu'une fonction indique une erreur en retournant à un autre emplacement. Fortran a soutenu cela via "retour alternatif":

C SUBROUTINE WITH ALTERNATE RETURN.  THE '*' IS A PLACE HOLDER FOR THE ERROR RETURN
      SUBROUTINE QSOLVE(A, B, C, X1, X2, *)
      DISCR = B*B - 4*A*C
C NO SOLUTIONS, RETURN TO ERROR HANDLING LOCATION
      IF DISCR .LT. 0 RETURN 1
      SD = SQRT(DISCR)
      DENOM = 2*A
      X1 = (-B + SD) / DENOM
      X2 = (-B - SD) / DENOM
      RETURN
      END

C USE OF ALTERNATE RETURN
      CALL QSOLVE(1, 0, 1, X1, X2, *99)
C SOLUTION FOUND
      ...
C QSOLVE RETURNS HERE IF NO SOLUTIONS
99    PRINT 'NO SOLUTIONS'

Ces deux techniques étaient très sujettes aux erreurs. L'utilisation d'entrées alternatives a souvent laissé une variable non initialisée. L'utilisation d'autres déclarations posait tous les problèmes posés par une instruction GOTO, avec comme complication supplémentaire le fait que la condition de branche n'était pas adjacente à la branche, mais quelque part dans le sous-programme.

kevin cline
la source
39
Et n'oubliez pas le code spaghetti . Il n'était pas inconnu pour les sous-routines de se terminer en utilisant un GOTO au lieu d'un retour, en laissant les paramètres d'appel de la fonction et l'adresse de retour sur la pile. Une seule sortie a été promue comme moyen de convertir au moins tous les chemins de code en une instruction RETURN.
TMN
2
@TMN: au début, la plupart des machines n'avaient pas de pile matérielle. La récursivité n'était généralement pas prise en charge. Les arguments de sous-programme et l'adresse de retour ont été stockés dans des emplacements fixes adjacents au code de sous-programme. Le retour était juste un goto indirect.
Kevin Cline
5
@ Kevin: Oui, mais selon vous, cela ne veut même plus dire en quoi il a été inventé. (BTW, je suis en fait assez sûr que Fred a demandé quelle était la préférence pour l' interprétation actuelle de "Single Exit".) De plus, C a eu constavant la naissance de nombreux utilisateurs ici, donc plus besoin de constantes de capital même en C. Mais Java conservé toutes ces mauvaises vieilles habitudes C .
sbi
3
Les exceptions violent-elles donc cette interprétation de Single Exit? (Ou leur cousin plus primitif,? setjmp/longjmp)
Mason Wheeler
2
Bien que l’opération se soit interrogée sur l’interprétation actuelle du terme «aller simple», cette réponse est celle qui a les racines les plus historiques. En règle générale , il ne sert à rien d' utiliser un seul retour , à moins que vous ne vouliez que votre langue corresponde à celle du génial VB (pas .NET). Rappelez-vous simplement d'utiliser la logique booléenne sans court-circuit.
Acelent
912

Cette notion de SESE ( Single Entry, Single Exit ) provient de langages à gestion de ressources explicite , comme le C et l’assemblage. En C, un code comme celui-ci perdra des ressources:

void f()
{
  resource res = acquire_resource();  // think malloc()
  if( f1(res) )
    return; // leaks res
  f2(res);
  release_resource(res);  // think free()
}

Dans de telles langues, vous avez essentiellement trois options:

  • Répliquez le code de nettoyage.
    Pouah. La redondance est toujours mauvaise.

  • Utilisez a gotopour passer au code de nettoyage.
    Cela nécessite que le code de nettoyage soit la dernière chose dans la fonction. (Et c’est pourquoi certains prétendent que cela gotoa sa place. Et c’est vrai - en C.)

  • Introduisez une variable locale et manipulez le flux de contrôle.
    L'inconvénient est que le flux de commande manipulé par la syntaxe (pensez break, return, if, while) est beaucoup plus facile à suivre que le flux de contrôle manipulé par l'état des variables (parce que ces variables ont pas d' état quand vous regardez l'algorithme).

En assembleur, c'est encore plus étrange, car vous pouvez accéder à n'importe quelle adresse d'une fonction lorsque vous appelez cette fonction, ce qui signifie que vous disposez d'un nombre presque illimité de points d'entrée pour n'importe quelle fonction. (Parfois, cela est utile. De tels thunks sont une technique courante permettant aux compilateurs d'implémenter l' thisajustement de pointeur nécessaire pour appeler des virtualfonctions dans des scénarios d'héritage multiples en C ++.)

Lorsque vous devez gérer les ressources manuellement, exploiter les options permettant d'entrer ou de quitter une fonction n'importe où conduit à un code plus complexe, et donc à des bogues. Par conséquent, une école de pensée est apparue qui a propagé SESE, afin d'obtenir un code plus propre et moins de bugs.


Cependant, lorsqu'une langue comporte des exceptions, (presque) n'importe quelle fonction peut être abandonnée prématurément à (presque) n'importe quel moment. Vous devez donc prévoir le retour prématuré de toute façon. (Je pense que finallyc'est principalement utilisé pour cela en Java et using(lors de l'implémentation IDisposable, finallysinon) en C #; C ++ utilise à la place RAII .) Une fois que vous avez effectué cela, vous ne pouvez pas vous empêcher de nettoyer après vous-même en raison d'une returndéclaration précoce . l'argument le plus fort en faveur de SESE a disparu.

Cela laisse la lisibilité. Bien sûr, une fonction 200 LoC avec une demi-douzaine d' returninstructions saupoudrées au hasard n'est pas un bon style de programmation et ne permet pas un code lisible. Mais une telle fonction ne serait pas facile à comprendre sans ces retours prématurés.

Dans les langues où les ressources ne sont pas ou ne devraient pas être gérées manuellement, l'adhésion à l'ancienne convention SESE n'a que peu, voire aucune valeur. OTOH, comme je l'ai déjà expliqué, SESE rend souvent le code plus complexe . C'est un dinosaure qui (à l'exception du C) ne correspond pas à la plupart des langues actuelles. Au lieu d’aider le caractère compréhensible du code, il l’entrave.


Pourquoi les programmeurs Java s'en tiennent-ils à cela? Je ne sais pas, mais de mon point de vue (extérieur), Java a pris un grand nombre de conventions de C (où elles ont un sens) et les a appliquées à son monde OO (où elles sont inutiles ou carrément mauvaises), où il s’en tient maintenant. eux, peu importe les coûts. (Comme la convention pour définir toutes vos variables au début de la portée.)

Les programmeurs s'en tiennent à toutes sortes de notations étranges pour des raisons irrationnelles. (Les énoncés structurels profondément imbriqués - les "pointes de flèches" - étaient, dans des langages comme Pascal, considérés jadis comme un code magnifique.) L'application d'un raisonnement purement logique à cet objectif ne semble pas convaincre la majorité d'entre eux de s'écarter de leurs méthodes habituelles. La meilleure façon de changer de telles habitudes est probablement de leur apprendre très tôt à faire ce qui est le mieux, et non ce qui est conventionnel. En tant que professeur de programmation, vous l’avez entre les mains.:)

sbi
la source
52
Droite. En Java, le code de nettoyage appartient aux finallyclauses dans lesquelles il est exécuté, qu’il soit returnou non à l’origine.
dan04
15
@ dan04 en Java 7, vous n’avez même pas besoin de la finallyplupart du temps.
R. Martinho Fernandes
93
@ Steven: Bien sûr, vous pouvez le démontrer! En fait, vous pouvez afficher du code compliqué et complexe avec toutes les fonctions pouvant également être affichées pour rendre le code plus simple et plus facile à comprendre. Tout peut être abusé. Le but est d’écrire du code pour qu’il soit plus facile à comprendre , et lorsque cela implique de jeter SESE par la fenêtre, qu’il en soit, et de foutre les vieilles habitudes qui s’appliquaient à différentes langues. Mais je n'hésiterais pas à contrôler l'exécution à l'aide de variables si je pensais que cela facilitait la lecture du code. C'est juste que je ne me souviens pas d'avoir vu un tel code depuis presque deux décennies.
sbi
21
@Karl: En effet, les langages GC, tels que Java, présentent une grave lacune: ils vous évitent d'avoir à nettoyer une ressource, mais échouent avec toutes les autres. (C ++ résout ce problème pour toutes les ressources en utilisant RAII .) Mais je ne parle même de la mémoire seulement (je ne mets malloc()et free()dans un commentaire comme exemple), je parlais des ressources en général. Je n'impliquais pas non plus que GC résolve ces problèmes. (J'ai mentionné le C ++, qui n'a pas de GC prêt à l'emploi.) D'après ce que j'ai compris, Java finallyest utilisé pour résoudre ce problème.
sbi
10
@sbi: Pour une fonction (procédure, méthode, etc.), il est plus important que de ne pas avoir plus d'une page, c'est que la fonction a un contrat clairement défini; si ce n'est pas clair parce qu'il a été coupé pour satisfaire une contrainte de longueur arbitraire, alors c'est Bad. La programmation consiste à jouer contre des forces différentes, parfois contradictoires.
Donal Fellows
81

D'une part, les instructions à retour unique facilitent la journalisation, ainsi que les formes de débogage reposant sur la journalisation. Je me souviens de nombreuses fois où j'ai dû réduire la fonction en retour simple pour imprimer la valeur de retour en un point unique.

  int function() {
     if (bidi) { print("return 1"); return 1; }
     for (int i = 0; i < n; i++) {
       if (vidi) { print("return 2"); return 2;}
     }
     print("return 3");
     return 3;
  }

D'autre part, vous pouvez reformuler ceci dans function()cet appel _function()et enregistrer le résultat.

perreal
la source
31
J'ajouterais également que cela facilite le débogage car il suffit de définir un point d'arrêt pour intercepter toutes les sorties * de la fonction. Je pense que certains IDE vous permettent de placer un point d'arrêt sur l'accolade proche de la fonction pour faire la même chose. (* sauf si vous appelez la sortie)
Skizz
3
Pour une raison similaire, il est également plus facile d’étendre (d’ajouter à) la fonction, car il n’est pas nécessaire d’insérer votre nouvelle fonctionnalité avant chaque déclaration. Supposons par exemple que vous deviez mettre à jour un journal avec le résultat de l'appel de fonction.
JeffSahol
63
Honnêtement, si je mettais ce code à jour, je préférerais avoir une définition bien définie _function(), avec returndes emplacements appropriés, et une enveloppe nommée function()qui gère la journalisation superflue, plutôt qu’une seule function()logique avec une logique contournée pour que tous les retours s’intègrent dans une seule sortie. -point juste pour que je puisse insérer une déclaration supplémentaire avant ce point.
Ruakh
11
Dans certains débogueurs (MSVS), vous pouvez mettre un point d'arrêt sur la dernière accolade de fermeture
Abyx
6
impression! = débogage. Ce n'est pas du tout un argument.
Piotr Perak
53

"Single Entry, Single Exit" tire son origine de la révolution de la programmation structurée du début des années 1970, lancée par la lettre d'Edsger W. Dijkstra au rédacteur en chef " Déclaration de GOTO considérée comme nuisible ". Les concepts de la programmation structurée ont été exposés en détail dans le livre classique "Structured Programming" d'Ole Johan-Dahl, Edsger W. Dijkstra et Charles Anthony Richard Hoare.

"Déclaration GOTO considérée comme nuisible" est une lecture obligatoire, même aujourd'hui. La "programmation structurée" est datée, mais toujours très, très enrichissante, et devrait figurer en haut de la liste "À lire" de tout développeur, bien au-dessus de quoi que ce soit, par exemple Steve McConnell. (La section de Dahl détaille les bases des classes dans Simula 67, qui constituent la base technique des classes en C ++ et de toute la programmation orientée objet.)

John R. Strohm
la source
6
L'article a été écrit quelques jours avant C, quand les GOTO étaient très utilisés. Ils ne sont pas l'ennemi, mais cette réponse est certainement correcte. Une déclaration de retour qui n'est pas à la fin d'une fonction est effectivement un goto.
user606723
31
L'article a été écrit dans les jours où gotopourrait littéralement aller partout , comme à droite dans un point au hasard dans une autre fonction, sans passer par toute notion de procédures, des fonctions, une pile d'appels, etc. Aucun langage permet sain d' esprit que ces jours -ci avec une ligne droite goto. C's setjmp/ longjmpest le seul cas semi-exceptionnel que je connaisse, et même cela nécessite une coopération des deux côtés. (C'est un peu ironique que j'utilise le mot "exceptionnel", sachant que les exceptions font presque la même chose ...) En gros, l'article décourage une pratique qui est morte depuis longtemps.
cHao
5
Du dernier paragraphe de "Déclaration jugée nuisible": "dans [2], Guiseppe Jacopini semble avoir prouvé le caractère superflu (logique) de la déclaration" go to ". L'exercice visant à traduire un diagramme de flux arbitraire de manière plus ou moins mécanique moins d'un, cependant, n'est pas recommandé . Dans ce cas, l'organigramme résultant ne sera pas plus transparent que l'original. "
hugomg Le
10
Qu'est-ce que cela a à voir avec la question? Oui, le travail de Dijkstra a finalement débouché sur des langues SESE, et alors? Ainsi que le travail de Babbage. Et peut-être devriez-vous relire le document si vous pensez qu'il indique quoi que ce soit à propos de plusieurs points de sortie dans une fonction. Parce que ça ne marche pas.
Jalf
10
@ John, vous semblez essayer de répondre à la question sans y répondre. C'est une bonne liste de lecture, mais vous n'avez rien cité ni paraphrasé pour justifier votre affirmation selon laquelle cet essai et ce livre ont quelque chose à dire sur l'inquiétude du demandeur. En effet, en dehors des commentaires, vous n'avez rien dit de substantiel sur la question. Envisagez d'élargir cette réponse.
Shog9
35

Il est toujours facile de relier Fowler.

L'un des principaux exemples qui vont à l'encontre de SESE sont les clauses de sauvegarde:

Remplacer le conditionnel imbriqué par des clauses de garde

Utiliser les clauses de garde pour tous les cas particuliers

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
return result;
};  

                                                                                                         http://www.refactoring.com/catalog/arrow.gif

double getPayAmount() {
    if (_isDead) return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired) return retiredAmount();
    return normalPayAmount();
};  

Pour plus d'informations, voir page 250 de la refactorisation ...

Pieter B
la source
11
Autre mauvais exemple: il pourrait tout aussi bien être corrigé avec d’autres-si.
Jack
1
Votre exemple n'est pas juste. Que diriez-vous de ceci: double getPayAmount () {double ret = normalPayAmount (); if (_isDead) ret = deadAmount (); if (_isSeparated) ret =parCoiffé (); if (_isRetired) ret = retiredAmount (); retour ret; };
Charbel le
6
@Charbel Ce n'est pas la même chose. Si _isSeparatedet _isRetiredpeut être vrai (et pourquoi cela ne serait-il pas possible?), Vous retournez le mauvais montant.
hvd
2
@Konchog "Les conditions imbriquées fourniront un meilleur temps d'exécution que les clauses de protection ". Cela nécessite principalement une citation. J'ai des doutes sur sa véracité. Dans ce cas, par exemple, en quoi le retour anticipé est-il différent du court-circuit logique en termes de code généré? Même si cela importait, je ne peux pas imaginer un cas où la différence serait plus qu'un ruban infinitésimal. Vous appliquez donc une optimisation prématurée en rendant le code moins lisible, uniquement pour satisfaire un argument théorique non prouvé sur ce qui, selon vous, conduit à un code légèrement plus rapide. Nous ne faisons pas ça ici
underscore_d
1
@underscore_d, vous avez raison. cela dépend beaucoup du compilateur, mais cela peut prendre plus de place. Examinez les deux pseudo-assemblages et vous comprendrez pourquoi les clauses de garde proviennent de langages de haut niveau. "A" test (1); branch_fail end; test (2); branch_fail end; test (3); branch_fail end; {CODE} end: return; Test "B" (1); branch_good next1; revenir; next1: test (2); branch_good next2; revenir; next2: test (3); branch_good next3; revenir; next3: {CODE} return;
11

J'ai écrit un article sur ce sujet il y a quelque temps.

L'essentiel, c'est que cette règle provient de l'âge des langues qui ne disposent pas de récupération de place ou de gestion des exceptions. Aucune étude formelle n'a montré que cette règle conduit à un meilleur code dans les langues modernes. N'hésitez pas à l'ignorer chaque fois que cela conduira à un code plus court ou plus lisible. Les gars de Java qui insistent là-dessus sont aveuglément et aveugles, suivant une règle obsolète et inutile.

Cette question a également été posée sur Stackoverflow

Anthony
la source
Hey, je ne peux plus atteindre ce lien. Avez-vous une version hébergée dans un endroit toujours accessible?
Nic Hartley
Salut, QPT, bon endroit. J'ai rapporté le blog et mis à jour l'URL ci-dessus. Il devrait créer un lien maintenant!
Anthony le
Mais il y a plus que ça. Il est beaucoup plus facile de gérer un minutage d'exécution précis à l'aide de SESE. De toute façon, les conditions imbriquées peuvent souvent être refactorisées avec un commutateur. Il ne s'agit pas simplement de savoir s'il y a ou non une valeur de retour.
Si vous prétendez qu'il n'y a pas d'étude formelle à l'appui, il vous incombe de faire le lien avec celle qui va à l'encontre de cela.
Mehrdad
Mehrdad, s'il y a une étude formelle à l'appui, montrez-la. C'est tout. Insister sur les preuves, c'est déplacer le fardeau de la preuve.
Anthony
7

Un retour facilite le refactoring. Essayez d’exécuter la «méthode extract» dans le corps interne d’une boucle for contenant un retour, une rupture ou une suite. Cela échouera si vous avez interrompu votre flux de contrôle.

Le fait est que je suppose que personne ne prétend prétendre écrire du code parfait. Donc, le code est régulièrement en cours de refactoring pour être "amélioré" et étendu. Mon objectif serait donc de garder mon code aussi convivial que possible pour la refactorisation.

Souvent, le problème est que je dois reformuler complètement les fonctions si elles contiennent des disjoncteurs de flux de contrôle et si je souhaite n’ajouter que peu de fonctionnalités. Ceci est très sujet aux erreurs car vous modifiez tout le flux de contrôle au lieu d’introduire de nouveaux chemins pour des imbrications isolées. Si vous n'avez qu'un seul retour à la fin ou si vous utilisez des gardes pour sortir d'une boucle, vous avez bien sûr plus d'imbrication et plus de code. Mais vous bénéficiez des capacités de refactoring prises en charge par le compilateur et l'IDE.

oopexpert
la source
La même chose s'applique aux variables. Quelles sont les alternatives à l’utilisation de constructions control-flow telles que le retour anticipé.
Déduplicateur
Les variables ne vous empêcheront généralement pas de diviser votre code de manière à préserver le flux de contrôle existant. Essayez "extraire la méthode". Les IDE ne peuvent effectuer que des refactorisations de présélection de flux de contrôle car ils ne peuvent pas dériver de la sémantique de ce que vous avez écrit.
oopexpert
5

Considérez le fait que plusieurs instructions de retour équivalent à avoir GOTO dans une seule instruction de retour. C'est le même cas avec les déclarations de rupture. Ainsi, certains, comme moi, les considèrent comme des GOTO à toutes fins pratiques.

Cependant, je ne considère pas ces types de GOT comme néfastes et n'hésiterai pas à utiliser un véritable GOTO dans mon code si j'en trouve une bonne raison.

Ma règle générale est que les GOTO sont uniquement pour le contrôle de flux. Ils ne devraient jamais être utilisés pour une boucle, et vous ne devriez jamais aller «vers le haut» ou «vers l'arrière». (comment fonctionnent les pauses / retours)

Comme d’autres l’ont déjà mentionné, il convient de lire la déclaration de GOTO considérée comme préjudiciable
, mais gardez à l’esprit que cela a été écrit en 1970, lorsque les GOTO étaient largement surutilisés. Tous les GOTO ne sont pas nocifs et je ne découragerais pas leur utilisation tant que vous ne les utilisez pas à la place de constructions normales, mais plutôt dans le cas étrange où l'utilisation de constructions normales serait très gênante.

Je trouve que leur utilisation dans les cas d'erreur où vous avez besoin d'échapper à une zone en raison d'une défaillance qui ne devrait jamais se produire dans des cas normaux est parfois utile. Mais vous devriez également envisager de placer ce code dans une fonction distincte de sorte que vous puissiez simplement revenir plus tôt au lieu d'utiliser un GOTO ... mais parfois c'est aussi peu pratique.

utilisateur606723
la source
6
Toutes les constructions structurées qui remplacent les gotos sont implémentées en termes de goto. Boucles, par exemple "si" et "cas". Cela ne les rend pas mauvais, bien au contraire. En outre, il s'agit "d'intentions".
Anthony
Touche, mais cela ne diffère pas de mon point ... Cela rend juste mon explication légèrement fausse. tant pis.
user606723
GOTO devrait toujours être acceptable tant que (1) la cible est dans la même méthode ou fonction et (2) la direction est en avant dans le code (sautez du code) et (3) la cible ne se trouve pas dans une autre structure imbriquée (par exemple GOTO du milieu de l’affaire au milieu de l’autre cas). Si vous suivez ces règles, toutes les utilisations abusives de GOTO ont une odeur de code très forte à la fois visuellement et logiquement.
Mikko Rantalainen
3

Complexité Cyclomatique

J'ai vu SonarCube utiliser plusieurs instructions de retour pour déterminer la complexité cyclomatique. Donc, plus les déclarations de retour, plus la complexité cyclomatique est élevée

Changement de type de retour

Les retours multiples signifient que nous devons changer à plusieurs endroits de la fonction lorsque nous décidons de changer notre type de retour.

Sortie multiple

Il est plus difficile de déboguer car la logique doit être soigneusement étudiée en conjonction avec les instructions conditionnelles pour comprendre la cause de la valeur renvoyée.

Solution refactorisée

La solution à plusieurs instructions de retour consiste à les remplacer par un polymorphisme ayant un seul retour après avoir résolu l'objet d'implémentation requis.

Trieur
la source
3
Passer de plusieurs retours à définir la valeur de retour à plusieurs endroits n'élimine pas la complexité cyclomatique, il ne fait que l'unifier l'emplacement de sortie. Tous les problèmes que la complexité cyclomatique peut indiquer dans le contexte donné demeurent. "Il est plus difficile de déboguer car la logique doit être soigneusement étudiée en conjonction avec les instructions conditionnelles pour comprendre la cause de la valeur renvoyée" Cette fois encore, la logique ne change pas en unifiant le retour. Si vous devez étudier attentivement le code pour comprendre son fonctionnement, il doit être refondu, sans interruption.
WillD