Mon patron continue de mentionner avec nonchalance que les mauvais programmeurs utilisent break
et continue
en boucle.
Je les utilise tout le temps parce qu'ils ont un sens; laissez-moi vous montrer l'inspiration:
function verify(object) {
if (object->value < 0) return false;
if (object->value > object->max_value) return false;
if (object->name == "") return false;
...
}
Le point ici est que la fonction vérifie d’abord que les conditions sont correctes, puis exécute la fonctionnalité réelle. Même chose avec les boucles IMO:
while (primary_condition) {
if (loop_count > 1000) break;
if (time_exect > 3600) break;
if (this->data == "undefined") continue;
if (this->skip == true) continue;
...
}
Je pense que cela facilite la lecture et le débogage; mais je ne vois pas non plus d'inconvénient.
goto
) sont utiles dans certains cas.Réponses:
Lorsqu'elles sont utilisées au début d'un bloc, lors des premières vérifications, elles agissent comme des conditions préalables, alors c'est bien.
Lorsqu'ils sont utilisés au milieu du bloc, avec du code autour, ils agissent comme des pièges cachés, alors c'est mauvais.
la source
Vous pouvez lire l'article de 1974 de Donald Knuth intitulé Structured Programming (Programmation structurée) sous go to Statements ( Déclarations) , dans lequel il décrit diverses utilisations de la
go to
structure qui sont souhaitables sur le plan structurel. Ils incluent l’équivalent debreak
et descontinue
déclarations (de nombreuses utilisations dego to
in ont été développées dans des constructions plus limitées). Votre patron est-il du genre à appeler Knuth un mauvais programmeur?(Les exemples donnés me intéressent. En règle générale,
break
et necontinue
sont pas appréciées par les gens qui aiment une entrée et une sortie de tout morceau de code, et ce genre de personne fronce les sourcils aussi sur plusieursreturn
déclarations.)la source
Break
,Continue
etExit
comme des outils dans ma boîte à outils; Je les utilise là où cela rend le code plus facile à suivre, mais pas là où cela rendrait les choses plus difficiles à lire.Je ne crois pas qu'ils sont mauvais. L'idée qu'ils sont mauvais vient de l'époque de la programmation structurée. Elle est liée à l'idée qu'une fonction doit avoir un seul point d'entrée et un seul point de sortie, c'est-à-dire un seul
return
par fonction.Cela a du sens si votre fonction est longue et si vous avez plusieurs boucles imbriquées. Cependant, vos fonctions doivent être courtes et vous devez envelopper les boucles et leurs corps dans des fonctions courtes qui leur sont propres. Généralement, le fait de forcer une fonction à avoir un seul point de sortie peut entraîner une logique très complexe.
Si votre fonction est très courte, si vous avez une seule boucle, ou au pire deux boucles imbriquées, et si le corps de la boucle est très courte, alors il est très clair ce qu'un
break
oucontinue
fait. Il est également clair ce que font plusieursreturn
déclarations.Ces questions sont abordées dans "Clean Code" de Robert C. Martin et dans "Refactoring" de Martin Fowler.
la source
CALL P(X, Y, &10)
et, en cas d'erreur, la fonction pourrait passer le contrôle de cette instruction au lieu de revenir au point de l'appel.Les mauvais programmeurs parlent en absolu (tout comme Sith). Les bons programmeurs utilisent la solution la plus claire possible ( toutes choses étant égales par ailleurs ).
Utiliser pause et continuer rend souvent le code difficile à suivre. Mais si les remplacer rend le code encore plus difficile à suivre, alors c'est un mauvais changement.
L'exemple que vous avez donné est certainement une situation où les pauses et les suites doivent être remplacées par quelque chose de plus élégant.
la source
La plupart des gens pensent que c'est une mauvaise idée car le comportement n'est pas facilement prévisible. Si vous lisez le code et que vous voyez que
while(x < 1000){}
vous présumez qu'il va s'exécuter jusqu'à x> = 1000 ... Mais s'il y a des coupures au milieu, cela ne vaut pas, vous ne pouvez donc pas vraiment faire confiance à votre en boucle ...C’est la même raison pour laquelle les gens n’aiment pas GOTO: certes, il peut être bien utilisé, mais cela peut également conduire à un code spaghetti divin, où le code saute au hasard d’une section à l’autre.
Pour ma part, si je devais faire une boucle qui se cassait à plus d'une condition, je
while(x){}
basculerais alors X en faux si je devais m'éclater. Le résultat final serait le même et quiconque lirait le code saurait regarder de plus près les choses qui inversaient la valeur de X.la source
while(notDone){ }
approche.break;
parx=false;
ne rend pas votre code plus clair. Vous devez toujours rechercher cette déclaration dans le corps. Et dans le cas dex=false;
vous devrez vérifier que cela nex=true;
tombe pas plus bas.while (x < 1000)
je suppose que ça courra 1000 fois". Eh bien, il y a beaucoup de raisons pour lesquelles c'est faux, même six
c'est initialement zéro. Par exemple, qui ditx
est incrémenté précisément une fois au cours de la boucle et jamais modifié de toute autre manière? Même pour votre propre hypothèse, le fait que quelque chosex >= 1000
soit défini ne signifie pas que la boucle se terminera - elle peut être remise dans la plage avant que la condition ne soit vérifiée.Oui, vous pouvez [ré] écrire des programmes sans instructions break (ou revenir au milieu de boucles qui font la même chose). Mais vous devrez peut-être introduire des variables supplémentaires et / ou une duplication de code, ce qui rend généralement le programme plus difficile à comprendre. Pascal (le langage de programmation) était très mauvais surtout pour les programmeurs débutants pour cette raison. Votre patron veut essentiellement que vous programmiez dans les structures de contrôle de Pascal. Si Linus Torvalds était à votre place, il montrerait probablement le majeur à votre patron!
Il existe un résultat informatique appelé hiérarchie des structures de contrôle de Kosaraju, qui remonte à 1973 et qui est mentionné dans le célèbre article de Knuth sur le gotos de 1974. (Cet article de Knuth avait déjà été recommandé ci-dessus par David Thornley, à propos .) Ce que S. Rao Kosaraju a prouvé en 1973, c’est qu’il n’est pas possible de réécrire tous les programmes qui ont des sauts de profondeur n à plusieurs niveaux en programmes dont la profondeur de coupure est inférieure à n sans introduire de variables supplémentaires. Mais disons que ce n'est qu'un résultat purement théorique. (Ajoutez juste quelques variables supplémentaires?! Vous pouvez sûrement le faire pour faire plaisir à votre patron ...)
Ce qui est bien plus important du point de vue du génie logiciel, c’est un article plus récent, publié en 1995 par Eric S. Roberts, intitulé Sorties de boucle et programmation structurée: rouvrir le débat ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Roberts résume plusieurs études empiriques menées par d'autres avant lui. Par exemple, lorsqu'un groupe d'étudiants de type CS101 était invité à écrire du code pour une fonction implémentant une recherche séquentielle dans un tableau, l'auteur de l'étude disait ce qui suit à propos des étudiants qui utilisaient un break / return / goto pour sortir de la boucle de recherche séquentielle lorsque l'élément a été trouvé:
Roberts dit aussi que:
Oui, vous êtes peut-être plus expérimenté que les étudiants de CS101, mais sans utiliser l'instruction break (ou un retour équivalent depuis le milieu des boucles), vous finirez par écrire du code qui, bien que nominalement structuré soit assez poilu en termes de logique supplémentaire les variables et la duplication de code que quelqu'un, probablement vous-même, y mettra des bugs logiques tout en essayant de suivre le style de codage de votre patron.
Je vais aussi dire ici que le papier de Roberts est beaucoup plus accessible au programmeur moyen, donc une meilleure première lecture que celle de Knuth. C'est aussi plus court et couvre un sujet plus étroit. Vous pourriez probablement même le recommander à votre patron, même s'il est le responsable plutôt que le type CS.
la source
Je n'envisage pas d'utiliser l'une ou l'autre de ces mauvaises pratiques, mais en utiliser trop dans la même boucle devrait justifier de repenser la logique utilisée dans la boucle. Utilisez-les avec parcimonie.
la source
L'exemple que vous avez donné n'a pas besoin de pauses ni continue:
Mon «problème» avec les 4 lignes de votre exemple est qu’elles sont toutes au même niveau mais qu’elles font des choses différentes: certaines cassent, d’autres continuent ... Vous devez lire chaque ligne.
Dans mon approche imbriquée, plus vous approfondissez, plus le code devient "utile".
Mais, si au fond de vous trouviez une raison pour arrêter la boucle (autre que la condition principale), une pause ou un retour aurait son utilité. Je préférerais cela à l'utilisation d'un drapeau supplémentaire qui doit être testé dans les conditions de haut niveau. La pause / retour est plus direct; il vaut mieux énoncer l’intention que de définir une autre variable.
la source
<
comparaisons doivent correspondre<=
à la solution des POLa "méchanceté" dépend de la façon dont vous les utilisez. J'utilise généralement des ruptures dans les constructions en boucle SEULEMENT lorsque cela me permet d'économiser des cycles qui ne peuvent pas être sauvegardés par le refactoring d'un algorithme. Par exemple, parcourir une collection à la recherche d'un élément avec une valeur dans une propriété spécifique définie sur true. Si tout ce que vous avez besoin de savoir, c'est que l'un des éléments avait cette propriété définie sur true, une fois que vous obtenez ce résultat, une pause est utile pour terminer la boucle de manière appropriée.
Si l'utilisation d'une pause ne rend pas le code plus facile à lire, plus court à exécuter ou à enregistrer les cycles de traitement de manière significative, il est préférable de ne pas les utiliser. J'ai tendance à coder au "plus petit commun dénominateur" lorsque cela est possible pour m'assurer que toute personne qui me suit peut facilement consulter mon code et comprendre ce qui se passe (je ne réussis pas toujours à cela). Les ruptures réduisent cela car elles introduisent des points d’entrée / sortie impairs. Si elles sont mal utilisées, elles peuvent se comporter comme une déclaration "goto" inusitée.
la source
Absolument pas ... Oui, l'utilisation de
goto
est mauvaise car elle détériore la structure de votre programme et il est également très difficile de comprendre le flux de contrôle.Mais l'utilisation de déclarations telles que
break
etcontinue
sont absolument nécessaires de nos jours et considérées comme une mauvaise pratique de programmation.Et aussi pas si difficile à comprendre le flux de contrôle en cours d'utilisation de
break
etcontinue
. Dans des constructions telles queswitch
labreak
déclaration est absolument nécessaire.la source
La notion essentielle vient de pouvoir analyser sémantiquement votre programme. Si vous avez une seule entrée et une seule sortie, le calcul nécessaire pour indiquer les états possibles est considérablement plus facile que si vous devez gérer des chemins de béquille.
En partie, cette difficulté se traduit par la possibilité de raisonner conceptuellement sur votre code.
Franchement, votre deuxième code n'est pas évident. Qu'est-ce que ça fait? Est-ce que continuer 'continue', ou est-ce que ça suit la boucle? Je n'ai aucune idée. Au moins votre premier exemple est clair.
la source
Je remplacerais votre deuxième extrait de code par
pas pour des raisons de rareté - je pense réellement que c'est plus facile à lire et pour que quelqu'un comprenne ce qui se passe. En règle générale, les conditions de vos boucles doivent être contenues uniquement dans celles qui ne sont pas jonchées dans tout le corps. Cependant, il existe des situations où
break
et quicontinue
peuvent aider à la lisibilité.break
plus quecontinue
je pourrais ajouter: Dla source
foreach
boucle, car elle ne fera qu'itérer chaque élément d'une collection. Unefor
boucle est similaire en ce sens qu’elle ne doit pas avoir de point de terminaison conditionnel. Si vous avez besoin d'un point de terminaison conditionnel, vous devez utiliser unewhile
boucle.break
maximum à mon avis de la boucle.Je ne suis pas d'accord avec votre patron. Il y a des endroits appropriés pour
break
etcontinue
pour être utilisé. En fait, la raison pour laquelle les exceptions et la gestion des exceptions ont été introduites dans les langages de programmation modernes est que vous ne pouvez pas résoudre tous les problèmes en utilisant simplementstructured techniques
.En passant, je ne veux pas commencer une discussion religieuse ici, mais vous pourriez restructurer votre code pour qu'il soit encore plus lisible, comme ceci:
Sur une autre note latérale
Personnellement, je n'aime pas l'utilisation de
( flag == true )
in conditionals car, si la variable est déjà un booléen, vous introduisez une comparaison supplémentaire qui doit se produire lorsque la valeur du booléen a la réponse que vous souhaitez - à moins bien sûr que vous soyez certain que votre compilateur optimiser cette comparaison supplémentaire.la source
boolean
ou ce que signifie une terminologie concise / élégante, alors vous feriez peut-être mieux embaucher des responsables plus intelligents ;-)Je suis d'accord avec ton patron. Ils sont mauvais parce qu'ils produisent des méthodes avec une complexité cyclomatique élevée. Ces méthodes sont difficiles à lire et à tester. Heureusement, il existe une solution facile. Extrayez le corps de la boucle dans une méthode distincte, où "continue" devient "retour". "Retour" est préférable, car après "retour", c'est fini - vous ne vous inquiétez pas pour l'État local.
Pour "break", extrayez la boucle elle-même dans une méthode distincte en remplaçant "break" par "return".
Si les méthodes extraites nécessitent un grand nombre d'arguments, c'est une indication pour extraire une classe - collectez-les dans un objet contextuel.
la source
Je pense que c'est seulement un problème quand imbriqué profondément dans plusieurs boucles. Il est difficile de savoir à quelle boucle vous vous mettez. Il peut être difficile de suivre une poursuite aussi, mais je pense que la vraie peine vient des pauses - la logique peut être difficile à suivre.
la source
break 2
; pour d'autres, j'imagine que des drapeaux bool temporaires sont utilisésTant qu'ils ne sont pas utilisés en mode déguisé, comme dans l'exemple suivant:
Je vais bien avec eux. (Exemple vu dans le code de production, meh)
la source
Je n'aime aucun de ces styles. Voici ce que je préférerais:
Je n'aime vraiment pas utiliser
return
pour abandonner une fonction. Cela ressemble à un abus dereturn
.Utiliser
break
aussi n'est pas toujours clair à lire.Mieux encore pourrait être:
moins d'imbrication et les conditions complexes sont refactorisées en variables (dans un vrai programme, il faudrait évidemment avoir de meilleurs noms, évidemment ...)
(Tout le code ci-dessus est pseudo-code)
la source
break
etcontinue
. Terminer une boucle de façon anormale me semble bizarre et je ne l’aime pas.continue
signifie ignorer la fonctionnalité aller à la boucle suivante; pas "continuer avec l'exécution"Non, c'est un moyen de résoudre un problème, et il existe d'autres moyens de le résoudre.
De nombreux langages courants (Java, .NET (C # + VB), PHP, écrivez le vôtre) utilisent "break" et "continue" pour sauter des boucles. Ils ont tous deux "structuré goto (s)" phrases.
Sans eux:
Avec eux:
Notez que les codes "break" et "continue" sont plus courts et convertissent généralement les phrases "while" en "phrases" pour "ou" foreach ".
Les deux cas sont une question de style de codage. Je préfère ne pas les utiliser , car le style prolixe me permet de mieux contrôler le code.
En fait, je travaille dans certains projets, où il était obligatoire d'utiliser ces phrases.
Certains développeurs peuvent penser qu'ils ne sont pas necesarilly, mais hypothétiques, si nous devions les supprimer, nous devons également supprimer "while" et "do while" ("répéter jusqu'au", vous les gars pascal) ;-)
Conclusion, même si je préfère ne pas les utiliser, je pense que c’est une option, pas une mauvaise pratique de programmation.
la source
Je ne suis pas contre
continue
etbreak
en principe, mais je pense que ce sont des constructions de très bas niveau qui peuvent très souvent être remplacées par quelque chose d'encore meilleur.J'utilise C # comme exemple ici. Prenons le cas de la volonté de parcourir une collection, mais nous ne voulons que les éléments qui remplissent certains prédicats, et nous ne voulons pas faire plus de 100 itérations maximum.
Cela semble raisonnablement propre. Ce n'est pas très difficile à comprendre. Je pense cependant que cela gagnerait beaucoup à être plus déclaratif. Comparez-le à ce qui suit:
Peut-être que les appels Où et prendre ne devraient même pas être dans cette méthode. Peut-être que ce filtrage devrait être fait AVANT que la collection soit passée à cette méthode. Quoi qu’il en soit, en nous éloignant des éléments de bas niveau et en nous concentrant davantage sur la logique métier proprement dite, nous comprenons mieux ce qui nous intéresse réellement. Il devient plus facile de séparer notre code en modules cohésifs plus conformes aux bonnes pratiques de conception, etc. sur.
Des éléments de bas niveau subsisteront toujours dans certaines parties du code, mais nous voulons le cacher autant que possible, car il faut de l'énergie mentale pour pouvoir raisonner à la place des problèmes de l'entreprise.
la source
Code Complete contient une section intéressante sur l'utilisation
goto
et les retours multiples d'une routine ou d'une boucle.En général, ce n'est pas une mauvaise pratique.
break
oucontinue
dire exactement ce qui se passe ensuite. Et je suis d'accord avec ça.Steve McConnell (auteur de Code Complete) utilise presque les mêmes exemples que vous pour montrer les avantages de l'utilisation de différentes
goto
instructions.Cependant, une utilisation excessive
break
oucontinue
pourrait conduire à un logiciel complexe et non maintenable.la source