web-dev-qa-db-fra.com

Quelqu'un at-il / peut-il défier l'oncle Bob sur son amour de supprimer les "accolades inutiles"?

Je déteste référencer un contenu paywall, mais cette vidéo montre exactement de quoi je parle. Précisément 12 minutes dans Robert Martin regarde ceci:

Some code

Et dit "Une de mes choses préférées à faire est de se débarrasser des appareils orthodontiques inutiles" comme il le transforme en ceci:

More code

Il y a longtemps, dans une éducation très éloignée, on m'a appris à ne pas faire cela car cela facilite l'introduction d'un bogue en ajoutant une autre ligne en retrait pensant qu'il est contrôlé par le if quand ce n'est pas le cas.

Pour être juste envers Oncle Bob, il refactorise une longue Java vers de minuscules petites fonctions qui, je suis d'accord, sont beaucoup plus lisibles. Quand il a fini de la changer, (22.18) cela ressemble à ceci :

Yet more code

Je me demande si cela est censé valider la suppression des accolades. Je connais déjà le meilleure pratique . L'oncle Bob peut-il être mis au défi sur ce point? L'oncle Bob a-t-il défendu l'idée?

96
candied_orange

La lisibilité n'est pas une mince affaire.

Je suis d'un avis mitigé en ce qui concerne les accolades qui renferment une seule méthode. Je les supprime personnellement pour des choses comme les déclarations de retour sur une seule ligne, mais laisser de telles accolades nous a en fait mordu très fort au dernier endroit où j'ai travaillé. Quelqu'un a ajouté une ligne de code à une instruction if sans ajouter également les accolades nécessaires, et comme il s'agissait de C, il a planté le système sans avertissement.

Je ne mets jamais au défi quiconque est religieux de toujours utiliser des accolades après ce petit fiasco.

Je vois donc l'avantage de la lisibilité, mais je suis parfaitement conscient des problèmes qui peuvent survenir lorsque vous omettez ces accolades.

Je ne prendrais pas la peine d'essayer de trouver une étude ou l'opinion publiée de quelqu'un. Tout le monde en a un (une opinion, c'est-à-dire), et parce que c'est une question de style, une opinion est à peu près aussi bonne qu'une autre. Pensez au problème vous-même, évaluez les avantages et les inconvénients, et décidez-vous vous-même. Si la boutique pour laquelle vous travaillez a une norme de codage qui couvre ce problème, suivez-la.

48
Robert Harvey

Vous pouvez trouver plusieurs promotions publiées ou refus de styles sans attache à ici ou ici ou partout où les abris de vélo sont peints.

En vous éloignant des hangars à vélos, rappelez-vous le grand bug SSL OS X/iOS de 2014?

if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
    goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
    goto fail;

Oui, "causé" par des blocs sans accolade https://www.imperialviolet.org/2014/02/22/applebug.html

Les préférences peuvent dépendre du style d'accolade. Si je devais écrire

if (suiteSetup)
{
    includePage(mode, suiteSetup);
}

Je pourrais aussi être enclin à économiser de l'espace. Mais

if (suiteSetup) {
    includePage(mode, suiteSetup);
}

n'utilise qu'une seule ligne "supplémentaire".


Je sais que tu ne l'as pas demandé, mais si je travaille seul, je suis hérétique. Si je retire des bretelles, je préfère

if (suiteSetup != null) includePage(mode, suiteSetup);

Il n'a pas le même problème visuel que le bogue de style SSL iOS, mais enregistre encore plus de lignes "inutiles" que l'oncle Bob;) se lit bien si vous y êtes habitué et a une utilisation courante dans Ruby (includePage(mode, suiteSetup) unless suiteSetup.nil?). Eh bien, sachez qu'il y a beaucoup d'opinions.

131
Paul Draper

L'oncle Bob a de nombreuses couches de défense contre une telle erreur qui n'étaient pas aussi courantes lorsque "toujours utiliser des accolades" était la sagesse dominante:

  1. Une préférence personnelle forte pour les conditionnels à ligne unique, de sorte que ceux à plusieurs lignes se distinguent et reçoivent un examen supplémentaire.
  2. Un éditeur qui dépasse automatiquement lorsque vous insérez une deuxième ligne.
  3. Une suite complète de tests unitaires qu'il exécute constamment.
  4. Une suite complète de tests d'intégration.
  5. Un examen par les pairs effectué avant la fusion de son code.
  6. Une relance de tous les tests par un serveur d'intégration continue.
  7. Tests effectués par un service qualité produit.

Je pense que si quelqu'un publiait un défi à l'oncle Bob, il aurait une assez bonne réfutation avec les points ci-dessus. C'est l'une des erreurs les plus faciles à détecter tôt.

60
Karl Bielefeldt

Dans la plupart des cas, c'est une préférence personnelle, mais il y a certaines choses à considérer.

Bugs possibles

Bien que l'on puisse soutenir que les bogues causés par l'oubli d'accolades de complément sont rares, d'après ce que j'ai v - queilsfairearriveoccasionnellement (sans oublier le fameux IOS goto fail bug). Je pense donc que cela devrait être un facteur lors de l'examen de votre style de code (certains outils mettent en garde contre indentation trompeuse , donc cela dépend aussi de votre chaîne d'outils).

Code valide (qui se lit comme ça pourrait être un bug)

Même en supposant que votre projet ne souffre pas de tels bogues, lors de la lecture du code, vous pouvez voir des blocs de code qui ressemblent à pourraient être des bogues - mais ne le sont pas, prenant certains de vos cycles mentaux .

Nous commençons par:

if (foo)
    bar();

Un développeur ajoute un commentaire utile.

if (foo)
    // At this point we know foo is valid.
    bar();

Plus tard, un développeur s'enrichit.

if (foo)
    // At this point we know foo is valid.
    // This never fails but is too slow even for debug, so keep disabled.
    // assert(is_valid(foo));
    bar();

Ou ajoute un bloc imbriqué:

if (foo)
    while (i--) {
        bar(i);
        baz(i);
    }

Ou utilise une macro:

if (foo)
    SOME_MACRO();

"... Étant donné que les macros peuvent définir plusieurs lignes de code, la macro utilise-t-elle do {...} while (0) pour plusieurs lignes? Cela devrait être le cas dans notre guide de style mais je ferais mieux de vérifier au cas où!"


Les exemples ci-dessus sont tous du code valide, mais plus il y a de contenu dans le bloc de code, plus vous devez lire pour vous assurer qu'il n'y a pas d'erreur.

Peut-être que votre style de code définit que les blocs multi-lignes nécessitent une accolade (peu importe quoi, même s'ils ne sont pas du code), mais j'ai vu ces types de commentaires ajoutés en production code. Quand vous le lisez, il y a un petit doute que celui qui a modifié ces lignes pour la dernière fois a oublié d'ajouter une accolade, parfois je ressens le besoin de revérifier fonctionne comme prévu (en particulier lors de l'enquête sur un bogue dans cette zone de la code).

Bruit Diff

Une raison pratique d'utiliser des accolades pour les lignes simples est de réduire le bruit diff.

Autrement dit, changer:

if (foo)
    bar();

À:

if (foo) {
    bar();
    baz();
}

... fait apparaître la ligne conditionnelle dans un diff comme étant modifiée, ce qui ajoute une petite surcharge mais inutile.

  • les lignes apparaissent comme étant modifiées dans les révisions de code, si vos outils différents sont basés sur Word, vous pouvez facilement voir que seule l'accolade a changé, mais cela prend plus de temps pour vérifier si la ligne n'a pas changé du tout.
    Cela dit, tous les outils ne prennent pas en charge la différenciation basée sur Word, diff (svn, git, hg ... etc.) apparaîtra comme si toute la ligne avait changé, même avec des outils sophistiqués, parfois vous devrez peut-être rapidement regardez sur un diff basé sur une ligne simple pour voir ce qui a changé.
  • les outils d'annotation (tels que git blame) afficheront la ligne comme étant modifiée, ce qui rend le suivi de l'origine d'une ligne plus important pour trouver le changement réel.

Celles-ci sont à la fois petites et dépendent du temps que vous passez en révision de code ou en traçage qui engage des lignes de code modifiées.

Un inconvénient plus tangible d'avoir des changements de lignes supplémentaires dans un diff, leur plus forte probabilité que des changements dans le code provoquent des conflits qui fusionnent et doivent être résolus manuellement .


Il y a une exception à cela, pour les bases de code qui ont { Sur sa propre ligne - ce n'est pas un problème.

L'argument diff noise ne tient pas si vous écrivez dans ce style:

if (foo)
{
    bar();
    baz();
}

Cependant, ce n'est pas une convention aussi courante, donc en ajoutant principalement à la réponse pour être complet (ne suggérant pas que les projets devraient utiliser ce style).

32
ideasman42

Il y a des années, j'ai été amené à déboguer du code C. Le bug était fou à trouver, mais finalement il se résumait à une déclaration comme:

if (debug)
   foo (4);

Et il s'est avéré que la personne qui l'avait écrit avait défini foo comme une macro. Une macro contenant deux lignes de code. Et bien sûr, seule la première de ces deux lignes était soumise au if. (La deuxième ligne a donc été exécutée sans condition.)

Cela peut être absolument unique à C et à son préprocesseur - qui effectue des substitutions avant la compilation - mais je ne l'ai jamais oublié. Ce genre de chose vous laisse une marque, et pourquoi ne pas y jouer en toute sécurité - surtout si vous utilisez une variété de langues et de chaînes d'outils et que vous ne pouvez pas être sûr que de tels manigances ne sont pas possibles ailleurs?

Maintenant, j'indente et utilise des accolades différemment de tout le monde, apparemment. Pour une seule ligne if, je ferais:

if (debug) { foo (4); }

il ne faut donc pas de lignes supplémentaires pour inclure les accolades.

15
Wayne

"Oncle Bob" est autorisé à avoir son avis, vous êtes autorisé à avoir votre avis. Pas besoin de le défier.

Si vous voulez faire appel à l'autorité, prenez Chris Lattner. Dans Swift, les instructions if ont perdu leurs parenthèses, mais sont toujours accompagnées d'accolades. Pas de discussion, ça fait partie du langage. Donc, si "Oncle Bob" commence à supprimer les accolades, le code arrête la compilation.

Passer par le code de quelqu'un d'autre et "se débarrasser des accolades inutiles" est une mauvaise habitude. N'entraîne un travail supplémentaire que lorsque le code doit être révisé et lorsque des conflits sont inutilement créés. Peut-être que "Oncle Bob" est un programmeur si incroyablement bon qu'il n'a pas besoin de révisions de code? J'ai perdu une semaine de ma vie parce qu'un des meilleurs programmeurs a changé "if (p! = NULL)" en "if (! P)" sans révision de code, caché au pire endroit possible.

Il s'agit principalement d'un débat de style inoffensif. Les accolades ont l'avantage que vous pouvez insérer une autre ligne de code sans ajouter d'accolades. Comme une déclaration de journalisation ou un commentaire (s'il est suivi d'un commentaire suivi d'une déclaration, c'est tout simplement horrible). sur la même ligne que si présente l'inconvénient pratique que vous rencontrez des problèmes avec de nombreux débogueurs. Mais faites ce que vous préférez.

15
gnasher729

Mes raisons pour ne pas retirer les accolades sont les suivantes:

  1. réduire la fatigue de décision. Si vous utilisez toujours des accolades, vous n'avez jamais à décider si vous allez avoir besoin d'accolades ou non.

  2. réduire la traînée de développement: même si vous vous efforcez de éventuellement extraire toutes les lignes de logique multiples vers des méthodes, avoir à convertir un braceless if en une contreventée if pour ajouter de la logique est un peu ennuyeux de la traînée de développement. Il y a donc l'effort de supprimer les accolades et l'effort de les ajouter à nouveau lorsque vous avez besoin de plus de code. Minuscule, mais ennuyeux.

9
Michael Johnston

Un jeune collègue a déclaré que les orthèses que nous considérons comme redondantes et superflues lui sont en fait utiles. Je ne me souviens pas exactement pourquoi, mais si cela lui permet d'écrire un meilleur code plus rapidement, c'est à lui seul une raison de les conserver.

Fwiw, nous nous sommes mis d'accord sur un compromis selon lequel les mettre sur une seule ligne ne rend pas ces conditions préalables aussi courtes non lisibles/distrayantes pour moi. Par exemple.

if (!param) { throw invalid_argument (blahblah); }
if (n < 2) { return 0; }
// real code for function starts here...

Je souligne également que ces conditions préalables d'introduction sont souvent des instructions de contrôle du flux pour le corps (par exemple, un return), donc la peur d'ajouter une deuxième instruction qui est censée être dans la même condition et d'oublier d'écrire des accolades est théorique . Une deuxième déclaration sous la condition serait de toute façon un code mort et n'aurait aucun sens.

Je soupçonne que le problème maîtrise de la lecture est dû au fait que l'analyseur cérébral de la personne est câblé pour avoir les accolades dans le cadre de l'énoncé conditionnel. Ce n'est pas une représentation précise de la grammaire du C++, mais pourrait être un effet secondaire de l'apprentissage de certaines autres langues en premier, où c'est le cas.

0
JDługosz