Voici un exemple très simplifié . Ce n'est pas nécessairement une question spécifique à la langue, et je vous demande d'ignorer les nombreuses autres façons dont la fonction peut être écrite et les modifications qui peuvent y être apportées. . La couleur est d'un type unique
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
else
{
return "No you can't";
}
}
Beaucoup de gens que j'ai rencontrés, ReSharper, et ce type (dont le commentaire m'a rappelé que je cherchais à poser cette question depuis un moment) recommanderaient de refactoriser le code pour supprimer le else
bloc en laissant ceci:
(Je ne me souviens pas de ce que la majorité a dit, je n'aurais peut-être pas demandé cela autrement)
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
return "No you can't";
}
Question: Y a - t-il une augmentation de la complexité introduite en n'incluant pas le else
bloc?
J'ai l'impression que les else
États déclarent plus directement l'intention, en déclarant que le code dans les deux blocs est directement lié.
De plus, je trouve que je peux éviter des erreurs subtiles de logique, en particulier après des modifications de code à une date ultérieure.
Prenons cette variante de mon exemple simplifié (en ignorant le fait que l' or
opérateur étant donné qu'il s'agit d'un exemple volontairement simplifié):
bool CanLeaveWithoutUmbrella()
{
if(sky.Color != Color.Blue)
{
return false;
}
return true;
}
Quelqu'un peut désormais ajouter un nouveau if
bloc basé sur une condition après le premier exemple sans reconnaître immédiatement correctement que la première condition place une contrainte sur sa propre condition.
Si un else
bloc était présent, celui qui a ajouté la nouvelle condition serait obligé d'aller déplacer le contenu du else
bloc (et si d'une manière ou d'une autre ils le masquent, l'heuristique montrera que le code est inaccessible, ce qui n'est pas le cas dans le cas où l'un en if
contraint un autre) .
Bien sûr, il y a d'autres façons de définir l'exemple spécifique de toute façon, qui empêchent toutes cette situation, mais ce n'est qu'un exemple.
La longueur de l'exemple que j'ai donné peut fausser l'aspect visuel de cela, alors supposez que l'espace occupé par les crochets est relativement insignifiant pour le reste de la méthode.
J'ai oublié de mentionner un cas dans lequel je suis d'accord avec l'omission d'un bloc else, et c'est lorsque j'utilise un if
bloc pour appliquer une contrainte qui doit être logiquement satisfaite pour tout le code suivant, comme un contrôle nul (ou tout autre garde) .
la source
else
clause (à mon goût, il sera encore plus lisible en omettant les parenthèses inutiles. Mais je pense que l'exemple n'est pas bien choisi, car dans le cas ci-dessus, j'écriraisreturn sky.Color == Color.Blue
(sans if / else). Un exemple avec un type de retour différent de bool rendrait probablement cela plus clair.Réponses:
À mon avis, le bloc else explicite est préférable. Quand je vois ça:
Je sais que j'ai affaire à des options mutuellement exclusives. Je n'ai pas besoin de lire ce qui se trouve à l'intérieur des blocs if pour pouvoir le dire.
Quand je vois ça:
Il semble, à première vue, qu'il retourne faux et a un effet secondaire facultatif, le ciel n'est pas bleu.
Selon moi, la structure du deuxième code ne reflète pas ce que fait réellement le code. C'est mauvais. Choisissez plutôt la première option qui reflète la structure logique. Je ne veux pas avoir à vérifier la logique de retour / pause / continuer dans chaque bloc pour connaître le flux logique.
Pourquoi quelqu'un préférerait l'autre est un mystère pour moi, j'espère que quelqu'un prendra la position opposée m'éclairera avec sa réponse.
Je dirais que les conditions de garde sont bonnes dans le cas où vous avez quitté le chemin heureux. Une erreur ou un échec s'est produit qui empêche l'exécution ordinaire de continuer. Lorsque cela se produit, vous devez lever une exception, renvoyer un code d'erreur ou tout ce qui convient à votre langue.
Peu de temps après la version originale de cette réponse, un code comme celui-ci a été découvert dans mon projet:
En ne mettant pas le retour à l'intérieur d'elses, le fait qu'une déclaration de retour manquait a été ignoré. S'il avait été utilisé autrement, le code n'aurait même pas été compilé. (Bien sûr, la plus grande préoccupation que j'ai est que les tests écrits sur ce code étaient assez mauvais pour ne pas détecter ce problème.)
la source
else
bloc (cela peut être gênant lorsque je suis obligé de le faire juste pour rester en ligne avec les conventions d'une base de code). Moi aussi, je suis intéressé de voir le contre-point ici.} // else
où les autres iraient normalement comme compromis entre les deux.La principale raison de la suppression du
else
bloc que j'ai trouvé est une indentation excessive. Le court-circuitage deselse
blocs permet une structure de code beaucoup plus plate.De nombreuses
if
déclarations sont essentiellement des gardes. Ils empêchent le déréférencement de pointeurs nuls et autres "ne faites pas ça!" les erreurs. Et ils conduisent à une résiliation rapide de la fonction actuelle. Par exemple:Maintenant, il peut sembler qu'une
else
clause serait très raisonnable ici:Et en effet, si c'est tout ce que vous faites, ce n'est pas beaucoup plus en retrait que l'exemple ci-dessus. Mais, c'est rarement tout ce que vous faites avec des structures de données réelles à plusieurs niveaux (une condition qui se produit tout le temps lors du traitement de formats courants comme JSON, HTML et XML). Donc, si vous souhaitez modifier le texte de tous les enfants d'un nœud HTML donné, dites:
Les gardes n'augmentent pas l'indentation du code. Avec une
else
clause, tout le travail réel commence à se déplacer vers la droite:Cela commence à avoir un mouvement vers la droite significatif, et c'est un exemple assez trivial, avec seulement deux conditions de garde. Chaque garde supplémentaire ajoute un autre niveau d'indentation. Le vrai code que j'ai écrit en traitant XML ou en consommant des flux JSON détaillés peut facilement empiler 3, 4 ou plusieurs conditions de garde. Si vous utilisez toujours le
else
, vous vous retrouvez 4, 5 ou 6 niveaux en retrait. Peut-être plus. Et cela contribue certainement à un sentiment de complexité du code et à plus de temps passé à comprendre ce qui correspond à quoi. Le style de sortie rapide, plat et rapide élimine une partie de cette «structure excessive» et semble plus simple.Addendum Les commentaires sur cette réponse m'ont fait réaliser qu'il n'était peut-être pas clair que la manipulation NULL / vide n'est pas la seule raison des protections conditionnelles. Pas presque! Bien que cet exemple simple se concentre sur la gestion NULL / vide et ne contienne pas d'autres raisons, la recherche de structures profondes telles que XML et AST pour un contenu "pertinent", par exemple, comporte souvent une longue série de tests spécifiques pour éliminer les nœuds non pertinents et sans intérêt cas. Une série de tests "si ce nœud n'est pas pertinent, retour" provoquera le même type de dérive vers la droite que les gardes NULL / vides. Et dans la pratique, les tests de pertinence ultérieurs sont fréquemment associés à des gardes NULL / vides pour les structures de données correspondantes plus profondes recherchées - un double coup dur pour la dérive vers la droite.
la source
else
bloc superflu, lorsque j'utilise unif
bloc pour appliquer une contrainte qui doit être logiquement satisfaite pour tout le code suivant, comme un contrôle nul (ou tout autre garde).then
clause (comme les déclarations de garde successives), alors il n'y a pas de valeur particulière à éviter laelse
clause particulière . En effet, cela réduirait la clarté et pourrait être dangereux (en omettant de simplement énoncer la structure alternative qui est / devrait être là).Quand je vois ça:
Je vois un idiome commun . Un schéma commun , qui dans ma tête se traduit par:
Parce que c'est un idiome très courant en programmation, le programmeur qui a l'habitude de voir ce type de code a une «traduction» implicite dans sa tête chaque fois qu'il le voit, qui le «convertit» dans le bon sens.
Je n'ai pas besoin d'explicite
else
, ma tête «le met là» pour moi parce qu'il a reconnu le motif dans son ensemble . Je comprends la signification de l'ensemble du modèle commun, je n'ai donc pas besoin de petits détails pour le clarifier.Par exemple, lisez le texte suivant:
Tu as lu ça?
Si oui, vous vous trompez. Relisez le texte. Ce qui est réellement écrit est
Il y a deux "les" mots dans le texte. Alors pourquoi avez-vous manqué l'un des deux?
C'est parce que votre cerveau a vu un schéma commun et l'a immédiatement traduit dans sa signification connue. Il n'avait pas besoin de lire la chose entière détail par détail pour comprendre le sens, car il reconnaissait déjà le motif en le voyant. C'est pourquoi il a ignoré le supplément "le ".
Même chose avec l'idiome ci-dessus. Les programmeurs qui ont lu beaucoup de code sont utilisés pour voir ce modèle , de
if(something) {return x;} return y;
. Ils n'ont pas besoin d'explicationselse
pour comprendre sa signification, car leur cerveau «le met là pour eux». Ils le reconnaissent comme un modèle avec une signification connue: "ce qui est après l'accolade fermante ne fonctionnera que comme impliciteelse
de la précédenteif
".Donc, à mon avis, ce
else
n'est pas nécessaire. Mais utilisez simplement ce qui semble plus lisible.la source
Ils ajoutent un encombrement visuel dans le code, donc oui, ils pourraient ajouter de la complexité.
Cependant, l'inverse est également vrai, un refactoring excessif pour réduire la longueur du code peut également ajouter de la complexité (pas dans ce cas simple bien sûr).
Je suis d'accord avec la déclaration selon laquelle le
else
bloc énonce l'intention. Mais ma conclusion est différente, car vous ajoutez de la complexité dans votre code pour y parvenir.Je ne suis pas d'accord avec votre affirmation selon laquelle cela vous permet d'éviter de subtiles erreurs de logique.
Voyons un exemple, maintenant il ne devrait retourner vrai que si le ciel est bleu et qu'il y a une humidité élevée, que l'humidité n'est pas élevée ou si le ciel est rouge. Mais alors, vous confondez une accolade et la placez dans le mauvais
else
:Nous avons vu toutes ces erreurs stupides dans le code de production et cela peut être très difficile à détecter.
Je suggérerais ce qui suit:
Je trouve cela plus facile à lire, car je peux voir en un coup d'œil que la valeur par défaut est
false
.De plus, l'idée de forcer le déplacement du contenu d'un bloc pour insérer une nouvelle clause est sujette à des erreurs. Cela se rapporte en quelque sorte au principe ouvert / fermé (le code doit être ouvert pour l'extension mais fermé pour la modification). Vous forcez à modifier le code existant (par exemple, le codeur peut introduire une erreur dans l'équilibrage des accolades).
Enfin, vous amenez les gens à répondre d'une manière prédéterminée que vous pensez être la bonne. Par exemple, vous présentez un exemple très simple mais après, vous déclarez que les gens ne devraient pas le prendre en considération. Cependant, la simplicité de l'exemple affecte toute la question:
Si le cas est aussi simple que celui présenté, alors ma réponse serait d'omettre toutes les
if else
clauses.return (sky.Color == Color.Blue);
Si l'affaire est un peu plus compliquée, avec diverses clauses, je répondrais:
bool CanLeaveWithoutUmbrella () {
}
Si le cas est compliqué et que toutes les clauses ne retournent pas vrai (peut-être qu'elles renvoient un double), et que je veux introduire une commande de point d'arrêt ou de connexion, je considérerais quelque chose comme:
}
Si nous ne parlons pas d'une méthode qui renvoie quelque chose, mais plutôt d'un bloc if-else qui définit une variable ou appelle une méthode, alors oui, j'inclurais une
else
clause finale .Par conséquent, la simplicité de l'exemple est également un facteur à considérer.
la source
Bien que le cas if-else décrit ait la plus grande complexité, dans la plupart des situations pratiques (mais pas toutes), cela n'a pas beaucoup d'importance.
Il y a des années, lorsque je travaillais sur un logiciel utilisé pour l'industrie aéronautique (il devait passer par un processus de certification), la vôtre était une question qui a été soulevée. Il s'est avéré qu'il y avait un coût financier à cette déclaration «else» car elle augmentait la complexité du code.
Voici pourquoi.
La présence de l '«autre» a créé un 3ème cas implicite qui a dû être évalué - celui de ni le «si» ni le «sinon» pris. Vous pensez peut-être (comme je l'étais à l'époque) WTF?
L'explication est allée comme ça ...
D'un point de vue logique, il n'y a que les deux options - le «si» et le «sinon». Cependant, ce que nous certifions était le fichier objet que le compilateur générait. Par conséquent, lorsqu'il y avait un «autre», une analyse devait être effectuée pour confirmer que le code de branchement généré était correct et qu'un mystérieux 3ème chemin n'apparaissait pas.
Comparez cela au cas où il n'y a qu'un «si» et pas un «autre». Dans ce cas, il n'y a que deux options - le chemin «si» et le chemin «non-si». Deux cas à évaluer est moins complexe que trois.
J'espère que cela t'aides.
la source
Le but le plus important du code est d'être compréhensible comme engagé (par opposition à facilement refactorisé, ce qui est utile mais moins important). Un exemple Python légèrement plus complexe peut être utilisé pour illustrer ceci:
C'est assez clair - c'est l'équivalent d'une recherche de
case
déclaration ou de dictionnaire, la version de niveau supérieur dereturn foo == bar
:C'est plus clair, car même si le nombre de jetons est plus élevé (32 vs 24 pour le code d'origine avec deux paires clé / valeur), la sémantique de la fonction est désormais explicite: ce n'est qu'une recherche.
(Cela a des conséquences pour la refactorisation - si vous vouliez avoir un effet secondaire si
key == NIK
, vous avez trois choix:if
/elif
/else
et insérez une seule ligne dans lekey == NIK
boîtier.if
instruction àvalue
pour le cas spécial avant de revenir.if
déclaration dans l' appelant.Nous voyons maintenant pourquoi la fonction de recherche est une puissante simplification: elle fait de la troisième option la solution la plus simple, car en plus de générer une différence plus petite que la première, c'est la seule qui s'assure que
value
cela ne fait qu'une chose. )Pour en revenir au code OP, je pense que cela peut servir de guide pour la complexité des
else
instructions: le code avec uneelse
instruction explicite est objectivement plus complexe, mais le plus important est de savoir s'il rend la sémantique du code claire et simple. Et il faut y répondre au cas par cas, car chaque morceau de code a une signification différente.la source
case
ouswitch
déclarations) sont moins homogènes. Plusieurs options ne sont que des retours de valeur simples, mais un ou deux cas nécessitent un traitement supplémentaire. Et lorsque vous découvrez que la stratégie de recherche ne vous convient pas, vous devez réécrire dans une syntaxe entièrement différenteif
elif
...else
Je pense que cela dépend du type de
if
déclaration que vous utilisez. Certainesif
instructions peuvent être considérées comme des expressions et d'autres ne peuvent être considérées que comme des instructions de flux de contrôle.Votre exemple me ressemble à une expression. Dans les langages de type C, l'opérateur ternaire
?:
ressemble beaucoup à uneif
instruction, mais c'est une expression, et la partie else ne peut pas être omise. Il est logique qu'une branche else ne puisse pas être omise dans une expression, car l'expression doit toujours avoir une valeur.En utilisant l'opérateur ternaire, votre exemple ressemblerait à ceci:
Puisqu'il s'agit d'une expression booléenne, elle devrait vraiment être simplifiée jusqu'à
L'inclusion d'une clause else explicite rend votre intention plus claire. Les gardes peuvent avoir un sens dans certaines situations, mais en choisissant entre deux valeurs possibles, qui ne sont ni exceptionnelles ni inhabituelles, elles n'aident pas.
la source
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Une autre chose à considérer dans cet argument est les habitudes promues par chaque approche.
if / else recadre un échantillon de codage en termes de branches. Comme vous le dites, parfois cette explication est bonne. Il y a vraiment deux chemins d'exécution possibles et nous voulons le souligner.
Dans d'autres cas, il n'y a qu'un seul chemin d'exécution et toutes ces choses exceptionnelles dont les programmeurs doivent s'inquiéter. Comme vous l'avez mentionné, les clauses de garde sont parfaites pour cela.
Il y a, bien sûr, le cas 3 ou plus (n), où nous encourageons généralement l'abandon de la chaîne if / else et l'utilisation d'une instruction case / switch ou d'un polymorphisme.
Le principe directeur que je garde à l'esprit ici est qu'une méthode ou une fonction ne devrait avoir qu'un seul objectif. Tout code avec une instruction if / else ou switch est uniquement destiné à la branche. Il doit donc être absurdement court (4 lignes) et ne déléguer qu'à la bonne méthode ou produire le résultat évident (comme votre exemple). Lorsque votre code est aussi court, il est difficile de rater ces multiples retours :) Tout comme "goto considéré comme nuisible", toute instruction de branchement peut être utilisée abusivement, donc mettre une pensée critique dans d'autres instructions en vaut généralement la peine. Comme vous l'avez mentionné, le simple fait d'avoir un bloc else permet de regrouper le code. Vous et votre équipe devrez décider de ce que vous en pensez.
Ce que l'outil se plaint vraiment, c'est le fait que vous avez un retour / pause / lancer à l'intérieur du bloc if. Donc, en ce qui le concerne, vous avez écrit une clause de garde mais vous l'avez ratée. Vous pouvez choisir de rendre l'outil heureux ou vous pouvez "divertir" sa suggestion sans l'accepter.
la source
else
sémantiquement, les outils d'analyse de code recherchent généralement des instructions étrangères, car ils mettent souvent en évidence une mauvaise compréhension du flux de contrôle. L'else
après unif
qui revient est des bits perdus.if(1 == 1)
déclenche souvent le même type d'avertissement.Je pense que la réponse est que cela dépend. Votre exemple de code (comme vous le signalez indirectement) renvoie simplement l'évaluation d'une condition et est mieux représenté, comme vous le savez évidemment, comme une expression unique.
Afin de déterminer si l'ajout d'une condition else clarifie ou masque le code, vous devez déterminer ce que représente IF / ELSE. Est-ce une expression (comme dans votre exemple)? Si c'est le cas, cette expression doit être extraite et utilisée. Est-ce une condition de garde? Si c'est le cas, l'ELSE est inutile et trompeur, car il fait apparaître les deux branches comme équivalentes. Est-ce un exemple de polymorphisme procédural? Extrayez-le. Établit-il des conditions préalables? Ensuite, cela peut être essentiel.
Avant de décider d'inclure ou d'éliminer l'ELSE, décidez ce qu'il représente, cela vous dira s'il doit être présent ou non.
la source
La réponse est un peu subjective. Un
else
bloc peut faciliter la lisibilité en établissant qu'un bloc de code s'exécute exclusivement vers un autre bloc de code, sans avoir à lire les deux blocs. Cela peut être utile si, disons, les deux blocs sont relativement longs. Il y a des cas, cependant, où avoirif
s sanselse
s peut être plus lisible. Comparez le code suivant avec un certain nombre deif/else
blocs:avec:
Le deuxième bloc de code transforme les
if
instructions imbriquées en clauses de garde. Cela réduit l'indentation et rend certaines des conditions de retour plus claires ("sia != b
, alors nous revenons0
!")Il a été souligné dans les commentaires qu'il peut y avoir quelques trous dans mon exemple, je vais donc l'étoffer un peu plus.
Nous aurions pu écrire le premier bloc de code ici comme ça pour le rendre plus lisible:
Ce code est équivalent aux deux blocs ci-dessus, mais il présente certains défis. La
else
clause ici relie ces instructions if ensemble. Dans la version de clause de garde ci-dessus, les clauses de garde sont indépendantes les unes des autres. Ajouter un nouveau signifie simplement écrire un nouveauif
entre le dernierif
et le reste de la logique. Ici, cela peut aussi être fait, avec seulement une légère charge cognitive supplémentaire. La différence, cependant, est quand une logique supplémentaire doit se produire avant cette nouvelle clause de garde. Lorsque les déclarations étaient indépendantes, nous pouvions faire:Avec la
if/else
chaîne connectée , ce n'est pas si facile à faire. En fait, la voie la plus simple à prendre serait de briser la chaîne pour ressembler davantage à la version de la clause de garde.Mais vraiment, cela a du sens. Une utilisation
if/else
faible implique une relation entre les parties. Nous pourrions supposer que celaa != b
est en quelque sorte lié àc > d
laif/else
version chaînée. Cette supposition serait trompeuse, car nous pouvons voir dans la version de la clause de garde qu'ils ne sont en fait pas liés. Cela signifie que nous pouvons faire plus avec des clauses indépendantes, comme les réorganiser (peut-être pour optimiser les performances des requêtes ou pour améliorer le flux logique). Nous pouvons également les ignorer cognitivement une fois que nous les avons dépassés. Dans uneif/else
chaîne, je suis moins sûr que la relation d'équivalence entrea
etb
ne reviendra pas plus tard.La relation impliquée par
else
est également apparente dans l'exemple de code profondément imbriqué, mais d'une manière différente. Leselse
instructions sont séparées du conditionnel auquel elles sont attachées et elles sont liées dans l' ordre inverse aux conditionnelles (la premièreelse
est attachée à la dernièreif
, la dernièreelse
est attachée à la premièreif
). Cela crée une dissonance cognitive où nous devons revenir en arrière dans le code, en essayant d'aligner l'indentation dans notre cerveau. C'est un peu comme essayer de faire correspondre un tas de parenthèses. Cela empire encore si l'indentation est incorrecte. Les accolades facultatives n'aident pas non plus.C'est une belle concession, mais cela n'invalide vraiment aucune réponse. Je pourrais dire que dans votre exemple de code:
une interprétation valable pour écrire un code comme celui-ci pourrait être que «nous ne devons partir avec un parapluie que si le ciel n'est pas bleu». Ici, il y a une contrainte que le ciel doit être bleu pour que le code suivant soit valide pour s'exécuter. J'ai rencontré la définition de votre concession.
Et si je dis que si la couleur du ciel est noire, c'est une nuit claire, donc pas de parapluie nécessaire. (Il existe des moyens plus simples d'écrire ceci, mais il existe des moyens plus simples d'écrire l'exemple entier.)
Comme dans l'exemple plus large ci-dessus, je peux simplement ajouter la nouvelle clause sans trop y penser. Dans un
if/else
, je ne peux pas être sûr de ne pas gâcher quelque chose, surtout si la logique est plus compliquée.Je soulignerai également que votre exemple simple n'est pas si simple non plus. Un test pour une valeur non binaire n'est pas le même que l'inverse de ce test avec des résultats inversés.
la source
else
bloc est essentielle pour la lisibilité.Vous avez trop simplifié votre exemple. L' une ou l'autre alternative peut être plus lisible, en fonction de la situation plus large: en particulier, cela dépend si l'une des deux branches de la condition est anormale . Permettez-moi de vous montrer: dans le cas où l'une ou l'autre branche est également probable, ...
... est plus lisible que ...
... parce que le premier présente une structure parallèle et le second non. Mais, lorsque la majeure partie de la fonction est consacrée à une tâche et qu'il vous suffit de vérifier d'abord certaines conditions préalables, ...
}
... est plus lisible que ...
... car délibérément ne pas mettre en place une structure parallèle fournit un indice à un futur lecteur qu'obtenir ici une entrée "vraiment de type deux" est anormal . (Dans la vraie vie,
ProcessInputDefinitelyOfTypeOne
ce ne serait pas une fonction distincte, donc la différence serait encore plus flagrante.)la source
if
-> Faites l'exceptionnel et laissez« comme une stratégie de retour anticipé . Pensez au code, dans lequel la valeur par défaut comprend plus de contrôles, il serait plus rapide de traiter le cas exceptionnel en premier.when using an if block to apply a constraint that must be logically satisfied for all following code, such as a null-check (or any other guards)
. Logiquement, tout travailProcessInputOfTypeOne
dépend du fait que!InputIsReallyTypeTwo(input)
nous devons donc le rattraper en dehors de notre traitement normal. Normalement, ceProcessInputOfTypeTwo(input);
serait vraimentthrow ICan'tProccessThatException
ce qui rendrait la similitude plus évidente.u+
" introduit généralement un jeton "plage unicode", mais si le caractère suivant n'est pas un chiffre hexadécimal, il faut alors sauvegarder et traiter le «u» comme identifiant à la place. Ainsi, la boucle du scanner principal est distribuée pour "scanner un jeton de plage unicode" de manière quelque peu imprécise, et cela commence par "... si nous sommes dans ce cas spécial inhabituel, sauvegardez, puis appelez plutôt le sous-programme scan-an-identifier".ProcessInputOfTypeOne
utilise une vérification imprécise mais rapide qui parfois attrape plutôt le type deux.Oui, la complexité augmente car la clause else est redondante . Il vaut donc mieux laisser de côté pour garder le code maigre et méchant. Cela revient au même que d'omettre les
this
qualificatifs redondants (Java, C ++, C #) et d'omettre les parenthèses dans lesreturn
instructions, etc.Un bon programmeur pense "que puis-je ajouter?" tandis qu'un grand programmeur pense "que puis-je supprimer?".
la source
else
bloc, si cela est expliqué dans une ligne (ce qui n'est pas souvent), c'est souvent avec ce genre de raisonnement, donc +1 pour présenter l'argument "par défaut" que je vois, mais je ne le fais pas trouver que c'est un raisonnement assez fort.A good programmer things "what can I add?" while a great programmer things "what can I remove?".
semble être un adage commun selon lequel beaucoup de gens surexploitent sans examen attentif, et je trouve personnellement que c'est un tel cas.J'ai remarqué, changeant d'avis sur cette affaire.
Lorsqu'on m'a posé cette question il y a environ un an, j'aurais répondu quelque chose comme:
»Il ne devrait y avoir qu'un
entry
et unexit
dans une méthode« Et dans cet esprit, j'aurais suggéré de refactoriser le code pour:Du point de vue de la communication, il est clair ce qui doit être fait.
If
quelque chose est le cas, manipulez le résultat d'une manière ,else
manipulez-le d'une autre manière .Mais cela implique un inutile
else
. Leelse
exprime le cas par défaut . Donc, dans un deuxième temps, je pourrais le refactoriserLa question se pose alors: pourquoi utiliser une variable temporaire? La prochaine étape de la refactorisation mène à:
C'est donc clair dans ce qu'il fait. J'irais même plus loin:
Ou pour les timides :
Vous pouvez le lire comme ça: »CanLeaveWithoutUmbrella retourne un bool . Si rien de spécial ne se produit, il renvoie false «C'est la première lecture, de haut en bas.
Et puis vous "analysez" la condition »Si la condition est remplie, elle retourne vrai « Vous pouvez complètement ignorer le conditionnel et avoir compris ce que fait cette fonction dans le cas par défaut . Votre œil et votre esprit n'ont qu'à parcourir quelques lettres du côté gauche, sans lire la partie de droite.
Oui, c'est correcte. Mais cette information est redondante . Vous avez un cas par défaut et une "exception" qui est couverte par la
if
déclaration.Lorsque j'ai affaire à des structures plus complexes, je choisirais une autre stratégie, en omettant le
if
ou son frère laidswitch
du tout.la source
So this is clear in what it does...
et les développer? (Les cas précédents sont également d'une pertinence discutable). Je dis cela parce que c'est ce que la question nous demande d'examiner. Vous avez déclaré votre position, en omettant le bloc else, et c'est en fait la position qui nécessite plus d'explications (et celle qui m'a amené à poser cette question). De la question:I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Pour faire court, dans ce cas, se débarrasser du
else
mot-clé est une bonne chose. C'est totalement redondant ici, et selon la façon dont vous formatez votre code, cela ajoutera une à trois lignes complètement inutiles à votre code. Considérez ce qui est dans leif
bloc:Par conséquent, si le
if
bloc devait être saisi, le reste de la fonction n'est, par définition, pas exécuté. Mais si elle n'est pas conclue, puisqu'il n'y a pas d'autresif
instructions, le reste de la fonction est exécuté. Ce serait totalement différent si unereturn
déclaration n'était pas dans leif
bloc, auquel cas la présence ou l'absence d'unelse
bloc aurait un impact, mais ici, cela n'aurait pas d'impact.Dans votre question, après avoir inversé votre
if
condition et omiselse
, vous avez déclaré ce qui suit:Ils doivent alors lire le code un peu plus attentivement. Nous utilisons des langages de haut niveau et une organisation claire du code pour atténuer ces problèmes, mais l'ajout d'un
else
bloc complètement redondant ajoute du «bruit» et du «fouillis» au code, et nous ne devrions pas non plus avoir à inverser ou à inverser nosif
conditions. S'ils ne peuvent pas faire la différence, ils ne savent pas ce qu'ils font. S'ils peuvent faire la différence, ils peuvent toujours inverserif
eux-mêmes les conditions d' origine .C'est essentiellement ce qui se passe ici cependant. Vous revenez du
if
bloc, de sorte que laif
condition à évaluerfalse
est une contrainte qui doit être logiquement satisfaite pour tout le code suivant.Non, cela constituera une diminution de la complexité de votre code, et cela peut même constituer une diminution du code compilé, selon que certaines optimisations super triviales auront lieu ou non.
la source
Dans l'exemple spécifique que vous avez donné, c'est le cas.
Je pense que cela ne pourrait pas être plus simple que cela:
la source
if
déclaration. En fait, je décourage explicitement toute simplification supplémentaire en utilisant le code exact que vous avez utilisé. De plus, la complexité cyclomatique n'est pas augmentée par leelse
bloc.J'essaie toujours d'écrire mon code de telle sorte que l'intention soit aussi claire que possible. Votre exemple manque de contexte, je vais donc le modifier un peu:
Notre intention semble être de pouvoir partir sans parapluie lorsque le ciel est bleu. Cependant, cette simple intention est perdue dans une mer de code. Il existe plusieurs façons de faciliter la compréhension.
Tout d'abord, il y a votre question sur la
else
succursale. Cela ne me dérange pas de toute façon, mais j'ai tendance à laisser de côté leelse
. Je comprends l'argument d'être explicite, mais mon avis est que lesif
déclarations devraient encapsuler les branches «facultatives», commereturn true
celle. Je n'appellerais pas lareturn false
branche «facultative», car elle agit comme notre valeur par défaut. Quoi qu'il en soit, je vois cela comme un problème très mineur et beaucoup moins important que les suivants:Un problème beaucoup plus important est que vos succursales en font trop! Les succursales, comme tout, devraient être «aussi simples que possible, mais pas plus». Dans ce cas, vous avez utilisé une
if
instruction, qui se branche entre des blocs de code (collections d'instructions) alors que tout ce dont vous avez vraiment besoin pour créer une branche sont des expressions (valeurs). Cela est clair car a) vos blocs de code ne contiennent que des instructions uniques et b) ce sont les mêmes instructions (return
). Nous pouvons utiliser l'opérateur ternaire?:
pour réduire la branche à la seule partie différente:Nous atteignons maintenant la redondance évidente à laquelle notre résultat est identique
this.color == Color.Blue
. Je sais que votre question nous demande d'ignorer cela, mais je pense qu'il est vraiment important de souligner qu'il s'agit d'une façon de penser très procédurale: vous décomposez les valeurs d'entrée et construisez des valeurs de sortie. C'est bien, mais si possible, il est beaucoup plus puissant de penser en termes de relations ou de transformations entre l'entrée et la sortie. Dans ce cas, la relation est évidemment qu'ils sont les mêmes, mais je vois souvent du code procédural alambiqué comme celui-ci, qui masque une relation simple. Allons de l'avant et faisons cette simplification:La prochaine chose que je voudrais souligner est que votre API contient un double négatif: il est possible que ce
CanLeaveWithoutUmbrella
soit le casfalse
. Ceci, bien sûr, à SimplifieNeedUmbrella
êtretrue
, donc nous allons le faire:Nous pouvons maintenant commencer à réfléchir à votre style de codage. Il semble que vous utilisiez un langage orienté objet, mais en utilisant des
if
instructions pour créer des branches entre des blocs de code, vous avez fini par écrire des procédures code .Dans le code orienté objet, tous les blocs de code sont des méthodes et toutes les branches sont effectuées via une répartition dynamique , par exemple. en utilisant des classes ou des prototypes. On peut se demander si cela simplifie les choses, mais cela devrait rendre votre code plus cohérent avec le reste du langage:
Notez que l'utilisation d'opérateurs ternaires pour créer des branches entre les expressions est courante dans la programmation fonctionnelle .
la source
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
. En fait, vous utilisez la ligne de code exacte que je mentionne. De plus, bien que cette partie de la question soit hors sujet, je dirais que vous allez trop loin dans votre inférence. Vous avez radicalement changé le code.return false;
vselse { return false; }
tout en ne soucier de la polarité de la branche, l' envoi dynamique, ternaires, etc. Si vous écrivez code de procédure dans un langage OO, un changement radical est nécessaire;)if/else
instruction et quelle est la polarité des branches.