C'est une chose que je fais beaucoup ces derniers temps.
Exemple:
setCircle(circle, i, { current }) {
if (i == current) {
circle.src = 'images/25CE.svg'
circle.alt = 'Now picking'
} else if (i < current) {
circle.src = 'images/25C9.svg'
circle.alt = 'Pick failed'
} else if (i > current) {
circle.src = 'images/25CB.svg'
circle.alt = 'Pick chance'
}
}
Souvent, l'échelle if/else est beaucoup plus compliquée que cela ...
Voir la clause finale? C'est redondant. L'échelle est censée finalement capturer toutes les conditions possibles. Ainsi, il pourrait être réécrit comme ça:
setCircle(circle, i, { current }) {
if (i == current) {
circle.src = 'images/25CE.svg'
circle.alt = 'Now picking'
} else if (i < current) {
circle.src = 'images/25C9.svg'
circle.alt = 'Pick failed'
} else {
circle.src = 'images/25CB.svg'
circle.alt = 'Pick chance'
}
}
C'est ainsi que j'écrivais du code, mais je n'aime pas ce style. Ma plainte est que la condition dans laquelle la dernière partie du code sera exécutée n'est pas évidente d'après le code. J'ai donc commencé à écrire explicitement cette condition pour la rendre plus évidente.
Toutefois:
} else { // i > current
circle.src = 'images/25CB.svg'
circle.alt = 'Pick chance'
}
Suis-je en train de manquer quelque chose? Ou est-ce OK de faire ce que j'ai décrit ou est-ce une mauvaise idée?
Les deux approches sont valides. Mais regardons de plus près les avantages et les inconvénients.
Pour une chaîne if
avec des conditions triviales comme ici, cela n'a pas vraiment d'importance:
else
final, il est évident pour le lecteur de savoir dans quelle condition le reste est déclenché;else if
final, il est aussi évident pour le lecteur qu'aucun else
supplémentaire n'est nécessaire puisque vous avez tout couvert.Cependant, il existe de nombreuses chaînes if
qui reposent sur des conditions plus complexes, combinant des états de plusieurs variables, peut-être avec une expression logique complexe. Dans ce cas, c'est moins évident. Et voici la conséquence de chaque style:
else
: vous êtes sûr que l'une des branches est prise. S'il vous arrive d'oublier un cas, il passera par cette dernière branche, donc pendant le débogage, si la dernière branche a été choisie et que vous vous attendiez à autre chose, vous comprendrez rapidement.else if
: vous devez dériver la condition redondante à coder, ce qui crée une source d'erreur potentielle avec le risque de ne pas couvrir tous les cas. De plus, si vous avez manqué un cas, rien ne sera exécuté et il pourrait être plus difficile de découvrir que quelque chose manquait (par exemple, si certaines variables que vous vous attendiez à définir conservent des valeurs des itérations précédentes).La dernière condition redondante est donc une source de risque. C'est pourquoi je préfère suggérer d'aller à une finale else
.
Edit: codage haute fiabilité
Si vous développez avec une grande fiabilité en tête, vous pourriez être intéressé par une autre variante: compléter votre final explicite redondant else if
Avec un final else
afin de détecter toute situation inattendue.
Il s'agit d'un codage défensif. Il est recommandé par certaines spécifications de sécurité telles que SEI CERT ou MISRA . Certains outils d'analyse statique implémentent même cela sous la forme d'un règle qui est systématiquement vérifié (cela pourrait expliquer les avertissements de votre compilateur).
Ce qui manque jusqu'à présent dans les réponses est de savoir quel type d'échec est moins nocif.
Si votre logique est bonne, peu importe ce que vous faites, le cas important est ce qui se passe si vous avez un bug.
Vous omettez le conditionnel final: l'option finale s'exécute même si ce n'est pas la bonne chose à faire.
Vous ajoutez simplement le conditionnel final: il n'exécute aucune option, selon la situation, cela peut simplement signifier que quelque chose ne s'affiche pas (faible préjudice), ou cela peut signifier une exception de référence nulle à un moment ultérieur (ce qui pourrait être un débogage douleur.)
Vous ajoutez le conditionnel final et une exception: il lève.
Vous devez décider laquelle de ces options est la meilleure. Dans le code de développement, je considère cela comme une évidence - prenez le troisième cas. Cependant, je définirais probablement circle.src sur une image d'erreur et circle.alt sur un message d'erreur avant de lancer - au cas où quelqu'un déciderait de désactiver les assertions plus tard, cela échouerait sans danger.
Une autre chose à considérer - quelles sont vos options de récupération? Parfois, vous n'avez pas de chemin de récupération. Ce que je pense en est l'exemple ultime, c'est le premier lancement de la fusée Ariane V. Une erreur non capturée/0 (en fait un débordement de division) a entraîné la destruction du booster. En réalité, le code qui s'est écrasé ne servait à rien à ce moment-là, il était devenu sans objet à l'instant où les boosters de ceinture s'allumaient. Une fois qu'ils ont allumé l'orbite ou le boom, vous faites de votre mieux, les erreurs ne peuvent pas être autorisées. (Si la fusée s'égare à cause de cela, le gars de la sécurité de la gamme tourne sa clé.)
Ce que je recommande, c'est d'utiliser une instruction assert
dans votre autre final, dans l'un de ces deux styles:
setCircle(circle, i, { current }) {
if (i == current) {
circle.src = 'images/25CE.svg'
circle.alt = 'Now picking'
} else if (i < current) {
circle.src = 'images/25C9.svg'
circle.alt = 'Pick failed'
} else {
assert i > current
circle.src = 'images/25CB.svg'
circle.alt = 'Pick chance'
}
}
Ou une assertion de code mort:
setCircle(circle, i, { current }) {
if (i == current) {
circle.src = 'images/25CE.svg'
circle.alt = 'Now picking'
} else if (i < current) {
circle.src = 'images/25C9.svg'
circle.alt = 'Pick failed'
} else if (i > current) {
circle.src = 'images/25CB.svg'
circle.alt = 'Pick chance'
} else {
assert False, "Unreachable code"
}
}
L'outil de couverture de code peut souvent être configuré pour ignorer le code tel que "affirmer faux" du rapport de couverture.
En plaçant la condition dans une assertion, vous documentez efficacement la condition d'une branche explicitement, mais contrairement à un commentaire, la condition d'assertion peut en fait être vérifiée et échouera si vous gardez les assertions activées pendant le développement ou en production (je recommande généralement de garder les assertions activées en production s'ils n'affectent pas trop les performances).
J'ai défini une macro "affirmée" qui évalue une condition, et dans une version de débogage tombe dans le débogueur.
Donc, si je suis sûr à 100% qu'une des trois conditions doit être remplie, j'écris
If condition 1 ...
Else if condition 2 .,,
Else if asserted (condition3) ...
Cela montre assez clairement qu'une condition sera vraie et qu'aucune branche supplémentaire pour une assertion n'est nécessaire.