Utiliser ELSE est-il une mauvaise programmation? [fermé]

18

J'ai souvent rencontré des bogues causés par l'utilisation de la ELSEconstruction. Un excellent exemple est quelque chose du genre:

If (passwordCheck() == false){
    displayMessage();
}else{
    letThemIn();
}

Pour moi, cela crie un problème de sécurité. Je sais que passwordCheck est probablement un booléen, mais je n'y placerais pas la sécurité de mes applications. Que se passerait-il si c'est une chaîne, un entier, etc.?

J'essaie généralement d'éviter d'utiliser ELSE, et plutôt d'opter pour deux instructions IF complètement distinctes pour tester ce que j'attends. Tout le reste est alors ignoré OU est spécifiquement géré.

C'est sûrement une meilleure façon d'empêcher les bugs / problèmes de sécurité d'entrer dans votre application.

Comment faites-vous les gars?

billy.bob
la source
3
Quel est le problème de sécurité pour vous? Que signifie "passwordCheck"? Il y a eu une vérification du mot de passe? Il doit y avoir une vérification du mot de passe? L'utilisateur a réussi? L'utilisateur n'a pas saisi le bon mot de passe?
LennyProgrammers
20
I know that passwordCheck is likely to be a boolean...Que voulez-vous dire? Dans n'importe quelle langue typée fort. passwordChecksera ce que vous voulez que ce soit.
Bobby
31
Je pense que les mauvaises pratiques d'indentation conduisent à plus d'erreurs que l'utilisation d' elseinstructions ...
gablin
15
Cela semble étrange. Tout d'abord, vous vous plaignez du type de retour passwordCheck()possible de ne pas être booléen (ce qui peut être une préoccupation raisonnable), puis vous le blâmez else? Je ne vois pas quels problèmes les elsecauses.
David Thornley
8
mmm, je pense que demander si l'utilisation d'autre est une mauvaise programmation est une mauvaise programmation
Muad'Dib

Réponses:

90

Le elsebloc doit toujours comprendre ce que vous voulez que le comportement par défaut soit.

Il n'est pas nécessaire de les éviter, veillez simplement à les utiliser de manière appropriée.

Dans votre exemple, l'état par défaut doit être de ne pas autoriser l'accès. Un peu de refactoring vous laisse:

If (passwordCheck)
{
   letThemIn();
}
else
{
   displayMessage();
}

c'est-à-dire si la vérification du mot de passe fonctionne, laissez-les entrer, sinon il est toujours valide d'afficher un message d'erreur.

Vous pouvez bien sûr ajouter des vérifications supplémentaires à votre logique en utilisant else ifdes ifinstructions plutôt que des instructions complètement séparées .

RYFN
la source
2
@ dave.b dans le contexte de l'exemple, je suppose que ce serait un problème de sécurité, mais si c'est partout dans la base de code que vous regardez, c'est plus un signe que celui qui l'a écrit a besoin d'un peu plus pratique :)
RYFN
9
Quelqu'un peut-il élaborer sur l'aspect sécurité de cela? if (! something) {do a} else {do ​​b} versus if (something) {do b} else {do ​​a} est logiquement équivalent non? J'essaie de comprendre quelle est la différence en termes de sécurité de cela?
Chris
11
@Chris: Je pense que l'OP utilise un langage faiblement typé. Par conséquent passwordCheckpourrait être quelque chose, fe null, ce qui rendrait passwordCheck == falseà falseet serait l'utilisateur permet de connexion en raison d'une erreur interne.
Bobby
1
@Chris, j'ai cru comprendre que l'état par défaut était d'autoriser l'accès, ce qui n'est pas nécessairement recommandé.
RYFN
3
Oui, cela dépend beaucoup de la langue. En C #, où ifnécessite une bool, les variables doivent être définitivement attribués, tous les chemins doivent renvoyer une valeur, etc., je ne peux pas penser à une raison quelconque l'ordre ifet elseimporterait en plus de lisibilité. Autrement dit, if(a){b();}{c();}devrait être équivalent à if(!a){c();{b();}. En JavaScript, en revanche, vous devez être conscient que cela passwordCheckpourrait être le cas undefined, etc.
Tim Goodman
57

Non, il n'y a rien de mal à cela ELSE. ELSEn'est pas le nouveau GOTO. En fait, l'utilisation de deux IFs au lieu de ELSEpeut entraîner plusieurs problèmes.

Exemple un:

if (this(is(a(very())==complex(check())))) {
   doSomething();
}

if (!this(is(a(very())==complex(check())))) {
   doTheOtherThing();
}

Vous voyez le copier-coller? Il attend juste le jour où vous changez l'un et oubliez l'autre.

Exemple deux:

foreach(x in y) {
  if (first_time) {
    first_time = false;
    doSomething();
  }

  if (!first_time) {
    doTheOtherThing();
  }
}

Comme vous pouvez le voir, le second IFsera également exécuté pour le premier élément, car la condition a déjà changé. Dans les programmes du monde réel, ces bogues sont plus difficiles à repérer.

user281377
la source
12
Je suis d'accord avec ça. Copier-coller une ifinstruction et inverser son expression booléenne juste pour éviter une else- maintenant c'est une mauvaise programmation! Non seulement vous dupliquez du code ( mauvaise programmation), mais vous ralentissez également les performances (si la vérification est vraiment compliquée, vous le faites maintenant deux fois - ba-- euh, eh bien, vous savez ce qu'ils disent à propos des optimisations prématurées de nos jours ... programmation pas si bonne !).
gablin
Pour être honnête, si la vérification doit être dans une méthode qui lui est propre pour retourner vrai / faux
billy.bob
De bons exemples, n'oublions pas les performances de l'utilisation de 2 si vérifie par rapport à un s'il est associé à un autre. Je suppose que dans la plupart des langues, l'utilisation d'un if / else fonctionnera mieux que l'utilisation de deux instructions if, l'une utilisant un pas à la condition.
Chris
1
dave.b: bien sûr, mais cela pourrait commencer simple et croître lentement; la vérification complexe dans mon exemple est là pour rendre le problème plus évident.
user281377
3
Ouais - et n'oubliez pas que les conditions peuvent avoir des effets secondaires dans la plupart des langues, et s'il y a des fonctions dans les conditions que vous ne pouvez vraiment pas voir en regardant.
David Thornley
18

Il y a toujours un AUTRE. Si vous écrivez

if(foo)
  bar();

tu écris

if(foo)
{
  bar();
}
else
{
   // do nothing
}

Tout ce que vous mettez dans le chemin ELSE est de votre responsabilité.

LennyProgrammers
la source
7
-1. Cette réponse est trop ridicule. Je ne dis pas que ce n'est pas vrai. Il manque juste le point. Ce que la compilation génère à partir de votre code n'a rien à voir avec les pratiques de codage et les bogues que vous écrivez
3
La question était "Est-ce que ELSE utilise une mauvaise programmation?". Ma réponse est: vous pouvez prétendre que ce n'est pas là, mais ne pas l'éviter.
LennyProgrammers
5
quelle langue ? en Java, le reste n'est pas dans le bytecode: P
IAdapter
@ acidzombie24 - c'est une très bonne pratique de considérer ce qu'est le «reste» de toute question. Parfois, c'est juste - faites tout le reste dans la fonction, mais cela montre que vous y avez pensé
Martin Beckett
14

Personnellement, j'ai tendance à éviter elseautant que possible, mais ce n'est pas pour un problème de sécurité .

Lors de la lecture de code, les instructions imbriquées rendent plus difficile le respect de la logique, car vous devez vous rappeler quel ensemble de conditions y mènera. Pour cette raison, je suis un grand fan de sortie anticipée:

if (checkPassword != OK) { displayMessage(); return; }

letThemIn();

Cela s'applique également aux boucles foret whiledans lesquelles je vais utiliser continueet breakchaque fois que cela évite un niveau d'indentation.

Chris Lattner le dit mieux que moi dans les normes de codage LLVM .

Matthieu M.
la source
Je suis d'accord. "else" crée une "fourchette" mentale et nous, les humains, sommes des créatures séquentielles.
user187291
Pour info, cela s'appelle une condition de garde
CaffGeek
@Chad: ah merci, toujours sympa d'avoir un nom pour les choses :)
Matthieu M.
1
+1. C'est la seule réponse que je puisse supporter qui a un score> 1. *writes an answer*
Je n'essaye pas d'éviter autre chose en tant que telle, mais je pense que je suis d'accord avec ce que vous écrivez. Le fait est que la chose que vous testez devrait être l'exception et non le cas normal ( stackoverflow.com/questions/114342/… ). Ici, l'échec de l'authentification est l'exception et (uniquement) qui doit être testée.
hlovdal
5

Il suffit ensuite de les remplacer,

If (passwordCheck == true)
{
     letThemIn();
}
else
{
     displayMessage();
}
Ahmet Kakıcı
la source
21
Et alors If ((passwordCheck == true) == true)? :-)
Hippo
1
Et bien c'est mon style, pour améliorer la lisibilité. Vous pouvez écrire if (! Value) mais je préfère if (value! = True)
Ahmet Kakıcı
7
Cela n'améliore pas la lisibilité. Cela ajoute juste du bruit. Pire encore, les programmeurs écrivent des constructions imbriquées simplement parce qu'ils ont peur des opérateurs booléens.
ak2
3
Si le nom de la variable est descriptif, il n'y a aucune raison pour que: if (someBooleanValue == true) soit nécessaire. Ce serait mieux: if (validPassword) {...
Mark Freedman
Eh bien, je viens de remplacer les blocs de code dans la question. Si vous lisez mon premier commentaire, j'ai dit que je préfère utiliser if (value! = True) au lieu de if (! Value). J'espère que vous pouvez voir la différence entre if (valeur) et if (! Valeur). Je voulais dire que je n'utilise pas d'opérateur complément. Sinon, vous avez raison, vrai signifie vrai.
Ahmet Kakıcı
3

Comme Matthieu M., je préfère une sortie anticipée à d'autres blocs profondément imbriqués ... Cela illustre une programmation bien défensive (si mauvaises conditions, inutile de continuer). Beaucoup de gens seront en désaccord avec nous, préférant un point de sortie unique; ce n'est pas l'objet du débat (je pense).

Maintenant, j'utilise certainement elsequand cela a du sens, en particulier pour des alternatives simples et courtes. Comme dit, la duplication du test est une perte de temps (programmeur et CPU), source de confusion et, plus tard, de bugs (quand l'un est changé, pas l'autre).
Parfois, j'ajoute un commentaire sur la elsepièce, rappelant quelle était la condition (en particulier si la ifpièce est longue, par exemple dans le code hérité) ou quelle est l'alternative.

Notez que certains partisans extrêmes de la programmation fonctionnelle proposent de s'en débarrasser entièrement if, en faveur de la correspondance de motifs ... Un peu trop extrême à mon goût. :-)

PhiLho
la source
1
comme remarque: si vous avez une construction if dans votre langage fonctionnel pur, alors vous avez vraiment besoin d'avoir une autre. Chaque expression doit renvoyer quelque chose!
Tokland
2

Il n'y a rien de mal à utiliser ELSE. Cependant, cela peut conduire à un code trop complexe, difficile à lire et à comprendre. Cela peut indiquer une mauvaise conception. Cela indique certainement des cas d'utilisation supplémentaires qui devront être testés.

Essayez de supprimer les ELSE si vous le pouvez - mais ne soyez pas paranoïaque à ce sujet. Steve McConnell appelle ce code en ligne droite dans Code Complete. C'est-à-dire qu'il existe un chemin clair simple à travers votre code.

Approches pour essayer votre problème particulier:

  • utiliser le polymorphisme. À la limite de votre système, validez les informations d'identification de sécurité de l'utilisateur. S'ils sont légitimes, renvoyez un objet de session - avec accès aux parties pertinentes du système ou lancez une exception. Cependant, cela pourrait rendre votre système plus complexe. Vous décidez donc de ce qui est plus facile à comprendre et à maintenir.

En général, les éléments suivants peuvent aider à réduire les ELSE dans votre code:

  • de bonnes exigences peuvent réduire la nécessité de telles décisions dans le code. Vous n'avez peut-être pas du tout besoin d'implémenter le cas d'utilisation.
  • conception plus claire. Cohésion maximale et minimisation du couplage. Cela garantit que les composants ne font pas double emploi avec les décisions prises dans d'autres composants.
  • gestion des exceptions pour gérer les cas d'erreur.
  • polymorphisme (voir l'exemple ci-dessus).
  • instructions switch - ce sont des ELSE glorifiées mais elles sont meilleures dans certaines situations.
Conor
la source
2
Je voudrais également inclure "Déplacer les vérifications booléennes complexes dans une fonction distincte".
gablin
2

Votre supposition que le code soit une fuite de sécurité peut ou non être vraie selon la langue que vous utilisez. Dans le code C, cela pourrait être un problème (en particulier parce qu'en C, un booléen est juste un entier non nul ou nul) - mais dans la plupart des langages fortement typés (c'est-à-dire la vérification du type d'exécution) si la passwordCheckvariable a été déclarée comme booléenne, il n'y a aucun moyen de lui attribuer autre chose. En fait, tout dans un ifprédicat doit se résoudre en booléen, que vous utilisiez les opérateurs booléens ou que vous utilisiez simplement la valeur. Si vous parvenez à avoir un autre type d'objet lié au passwordCheckruntime, cela lèverait un certain type d'exception de conversion illégale.

Les constructions if / else simples sont beaucoup plus faciles à lire que les constructions if / if - et moins sujettes à des problèmes par inadvertance si quelqu'un essaie de retourner la construction. Prenons le même exemple pendant une seconde:

if(passwordCheck == false) {
    denyAccess();
}

if(passwordCheck) {
    letThemIn();
}

La signification des clauses mutuellement exclusives que vous souhaitez exécuter ci-dessus est perdue. C'est ce que la construction if / else transmet. Deux branches d'exécution mutuellement exclusives, où l'une d'elles s'exécutera toujours. Il s'agit d'un élément important de la sécurité: il n'y a aucun moyen de le faire letThemInaprès avoir appelé denyAccess.

Pour des raisons de clarté du code et pour garantir que les sections critiques sont les mieux protégées, elles doivent se trouver à l'intérieur de la clause primaire (la ifpartie). Le comportement non conforme par défaut doit se trouver dans la clause alternative (la elsepartie). Par exemple:

if(passwordCheck) {
    letThemIn();
} else {
    denyAccess();
}

REMARQUE: en travaillant avec différentes langues, j'ai développé un habbit de codage qui permet d'éviter la question "et si c'est une chaîne?" Il s'agit essentiellement de mettre la constante en premier dans l'expression booléenne. Par exemple, au lieu de vérifier, passwordCheck == falseje vérifie false == passwordCheck. Cela évite également le problème d'affectation accidentelle possible en C ++. En utilisant cette approche, le compilateur se plaindra si je tape à la =place de ==. Dans des langages comme Java et C #, le compilateur traiterait l'affectation dans la clause if comme une erreur, mais C ++ l'acceptera volontiers. C'est pourquoi j'ai également tendance à faire une vérification nulle avec le nullpremier.

Si vous changez régulièrement de langue, placer la constante en premier est très utile. Cependant, dans mon équipe, il est opposé à la norme de codage et le compilateur détecte de toute façon ces problèmes. Cela peut être difficile à briser.

Berin Loritsch
la source
1

Dire que l'utilisation elselorsque la programmation est mauvaise, c'est comme dire que l'utilisation otherwiselorsque l'on parle est mauvaise.

Bien sûr, ils peuvent tous les deux être utilisés de mauvaise façon, mais cela ne signifie pas qu'ils doivent être évités simplement parce que vous avez fait une erreur qui les a inclus. Je ne serais pas surpris si de nombreux bugs dépendaient d'un defaultcas manquant dans une switchdéclaration.

gablin
la source
1

Considérez Elsecomme une liste blanche de votre flux d'application. Vous vérifiez les conditions qui DEVRAIENT permettre au flux d'application de continuer, et si elles ne sont pas remplies, votre Elseest exécuté pour résoudre le problème, interrompre l'exécution de l'application ou quelque chose de similaire.

Else en soi n'est pas mauvais, mais si vous l'utilisez mal, vous pouvez voir des effets indésirables.

En ce qui concerne également votre déclaration

"Je sais que passwordCheck est susceptible d'être un booléen, mais je n'y placerais pas la sécurité de mes applications."

Pour les méthodes que vous développez, TOUJOURS renvoyer un type de données. Bien que PHP Core soit jonché de code qui retourne deux types de données ou plus, c'est une mauvaise pratique car cela permet de deviner les appels de fonction. Si vous devez renvoyer plus d'un type de données, envisagez de lever une exception (je trouve que c'est souvent la raison pour laquelle je voudrais retourner un autre type de données - quelque chose s'est horriblement mal passé), ou envisagez de restructurer votre code afin que vous puissiez ne renvoie qu'un seul type de données.

Craige
la source
Je ne savais pas qu'il y avait des langues qui renvoyaient plus d'un type de données! Faites-vous référence à des fonctions polymorphes? Je crois que chaque fois que je surcharge une fonction, elle a toujours renvoyé le même type de données, même si cela peut prendre des arguments différents.
Michael K
1
Dans les langages mal typés, et en particulier en PHP, les fonctions peuvent renvoyer plus d'un type de données. par exemple: stristr- "Renvoie la sous-chaîne correspondante. Si l'aiguille n'est pas trouvée, renvoie FAUX"
Craige
@Michael, Une fonction PHP peut retourner tout ce que vous voulez. Il n'y a aucune restriction sur le type de données. Ma fonction la plus compliquée renvoie true / false / null, mais rien (sauf le bon sens) ne vous empêche d'écrire une fonction qui retourne true / integer / null / string / float / array.
TRiG
Intéressant. Je n'ai jamais travaillé avec PHP. Merci pour l'explication - aidez-moi probablement à l'avenir! un peu comme Javascript alors?
Michael K
@Michael - Assez similaire à Javascript en effet.
Craige
1

Tout d'abord. LOL! Il n'y a aucune raison d'éviter quoi que ce soit d'autre. Ce n'est PAS une mauvaise pratique en aucune façon, forme ou forme.

Si quelque chose le code doit être

if(!IsLoggedIn) { ShowBadLoginOrNoAccessPage(); return }

Il n'y a pas deux ifs et il n'y en a pas d'autre. C'est ce que je fais dans toutes mes applications sauf une dans laquelle je lève une exception. L'exception est interceptée dans ma fonction qui vérifie l'URL pour la page appropriée à afficher (ou bien je peux mettre la fonction d'erreur catch / check dans asp.net). Il imprime une page générique qui dit de ne pas autoriser ou quel que soit le message que j'utilise dans l'exception (je vérifie toujours le type d'exception et définit le code d'état http).

-Edit- comme le montre l'exemple ammoQ deux ifs est ridicule. Vraiment d' autre est tout aussi bon ou meilleur qu'un if. Si quelque chose doit être évité (même si je ne le fais pas personnellement. Mais j'utilise beaucoup le retour et la rupture), comme il a été dit, plus de chemins de code augmentent la probabilité de bugs. Voir la complexité cyclomatique

-Modifier 2- Si vous vous inquiétez de l'utilisation if / else. Je noterai également que ma préférence est de mettre le bloc de code le plus court en haut, comme

if(cond == false) {
    a()
    b()
    c()
    onetwothree()
}
else
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}

Plutôt que

if(cond) 
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}
else
{
    a()
    b()
    c()
    onetwothree()
}

la source
0

J'aime définir une valeur par défaut avant les conditions lorsque je le peux. J'ai l'impression que c'est un peu plus facile à lire et un peu plus explicite, mais ce n'est qu'une préférence. J'ai tendance à essayer d'éviter les conditions négatives dans mon code. Je ne suis pas un grand fan de la vérification de! Foo ou false == foo et j'ai l'impression que c'est une sorte d'équivalent conditionnel d'un négatif.

foo = bar;

if ('fubar' == baz) {
    foo = baz;
}

au lieu de ...

if ('fubar' == baz) {
    foo = baz;
} else {
    foo = bar;
}

Le bloc de code précédent semble juste un peu plus facile à lire pour moi. Il me semble plus naturel d'avoir une sorte de paranoïa sceptique sur mon code. Définir une valeur par défaut quelle que soit la condition me met à l'aise: P


la source
0

Je dirais que l'utilisation de la logique de branchement de toute nature doit être évitée autant que possible. Bien que rien ne cloche avec ELSE ou IF, il existe de nombreuses façons d'écrire du code pour minimiser la nécessité d'utiliser une logique de branchement. Je ne dis pas que la logique de branchement peut être totalement éliminée - elle sera nécessaire à certains endroits - mais il est possible de refactoriser le code pour en éliminer une bonne partie. Dans la plupart des cas, cela améliorera l'intelligibilité et la précision de votre code.

À titre d'exemple, les opérateurs ternaires sont également généralement de bons candidats:

If VIP Then 
  Discount = .25
Else
  Discount = 0
End If
Total = (1 - Discount) * Total

En utilisant une approche ternaire:

Discount = VIP ? .25 : 0
Total = (1 - Discount) * Total

Les opérateurs ternaires déplacent la branche vers la droite dans le bon sens.

Mario T. Lanza
la source