O ELSE está com programação ruim? [fechadas]

18

Muitas vezes me deparei com erros que foram causados ​​pelo uso da ELSEconstrução. Um excelente exemplo é algo como:

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

Para mim, isso grita um problema de segurança. Sei que o passwordCheck provavelmente será um booleano, mas não colocaria a segurança dos meus aplicativos nele. O que aconteceria se fosse uma string, int etc?

Normalmente, tento evitar o uso ELSEe, em vez disso, opto por duas instruções IF completamente separadas para testar o que espero. Qualquer outra coisa que seja ignorada OU será tratada especificamente.

Certamente, essa é a melhor maneira de impedir que bugs / problemas de segurança entrem no seu aplicativo.

Como vocês fazem isso?

billy.bob
fonte
3
Qual é o problema de segurança para você? O que significa "passwordCheck"? Houve uma verificação de senha? Precisa haver uma verificação de senha? O usuário passou? O usuário não conseguiu digitar a senha correta?
LennyProgrammers
20
I know that passwordCheck is likely to be a boolean...O que você quer dizer? Em qualquer linguagem de tipo forte. passwordCheckserá o que você quiser que seja.
Bobby
31
Eu acho que as práticas de recuo ruins levar a mais erros do que usar elsedeclarações ...
gablin
15
Isso parece estranho. Primeiro, você reclama do possível tipo de retorno de passwordCheck()não ser booleano (o que pode ser uma preocupação razoável) e depois o culpa else? Não vejo que problemas as elsecausas.
David Thornley
8
mmm, eu acho perguntando se usando o resto é má programação é má programação
Muad'Dib

Respostas:

90

O elsebloco deve sempre consistir no que você deseja que seja o comportamento padrão.

Não há necessidade de evitá-los, apenas tome cuidado para usá-los adequadamente.

No seu exemplo, o estado padrão deve ser o de não permitir acesso. Um pouco de refatoração deixa você com:

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

ou seja, se a verificação da senha funcionar, deixe-os entrar, caso contrário, é sempre válido mostrar alguma mensagem de erro.

Obviamente, você pode adicionar verificações adicionais à sua lógica usando instruções em else ifvez de ifinstruções completamente separadas .

RYFN
fonte
2
@ dave.b no contexto do exemplo, acho que seria um problema de segurança, mas se isso estiver em todo o lugar na base de código que você está vendo, é mais um sinal de quem o escreveu precisando de um pouco mais Practice :)
RYFN
9
Alguém pode elaborar sobre o aspecto de segurança disso? if (! something) {faça ​​a} else {faça ​​b} versus if (alguma coisa) {faça ​​b} else {faça ​​a} é logicamente equivalente, não? Estou tentando entender qual é a diferença em termos de segurança disso?
Chris
11
@ Chris: Eu acho que o OP usa uma linguagem de tipo fraco. Por isso passwordCheckpode ser qualquer coisa, fe null, o que tornaria passwordCheck == falsea falsee que o usuário deixe de login devido a um erro interno.
Bobby
1
@ Chris, meu entendimento era que o estado padrão era permitir o acesso, o que não é necessariamente aconselhável.
RYFN
3
Sim, isso depende muito do idioma. Em C #, onde ifrequer um bool, variáveis devem ser definitivamente atribuída, todos os caminhos deve retornar um valor, etc., eu não consigo pensar em qualquer motivo, a ordem de ife elseimportaria além legibilidade. Ou seja, if(a){b();}{c();}deve ser equivalente a if(!a){c();{b();}. Em JavaScript, por outro lado, você deve estar ciente de que passwordCheckpode ser undefinedetc.
Tim Goodman
57

Não, não há nada errado ELSE. ELSEnão é novo GOTO. De fato, usar dois IFs em vez de ELSEpode levar a vários problemas.

Exemplo um:

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

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

Você vê a copiar e colar? Apenas espera o dia em que você muda um e esquece o outro.

Exemplo dois:

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

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

Como você pode ver, o segundo IFtambém será executado para o primeiro item, porque a condição já foi alterada. Nos programas do mundo real, esses erros são mais difíceis de detectar.

user281377
fonte
12
Eu concordo com isto. Copiar e colar uma ifinstrução e inverter sua expressão booleana apenas para evitar uma else- agora que é uma programação ruim! Não só você está duplicação de código ( programação), você também está a abrandar o desempenho (se o cheque é realmente complicado, você está agora a fazê-lo duas vezes - ba-- uh, bem, você sabe o que eles dizem sobre otimizações prematuras hoje em dia ... não é tão boa programação!).
gablin
Para ser sincero, se o cheque deve ser um método próprio para retornar verdadeiro / falso
billy.bob
Bons exemplos, não vamos esquecer o desempenho de usar 2 se verificações versus um se emparelhado com outro. Eu diria que na maioria dos idiomas, usar um if / else terá um desempenho melhor do que o uso de duas instruções if com uma usando a não na condição.
Chris
1
dave.b: claro, mas poderia começar simples e crescer lentamente; a verificação complexa no meu exemplo está aqui para tornar o problema mais óbvio.
user281377
3
Sim - e não esqueça que as condições podem ter efeitos colaterais na maioria dos idiomas, e se houver funções nas condições, você realmente não pode perceber olhando.
David Thornley
18

Sempre há um ELSE. Se você escrever

if(foo)
  bar();

você realmente escreve

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

Tudo o que você colocar no caminho ELSE é de sua responsabilidade.

LennyProgrammers
fonte
7
-1. Essa resposta é ridícula demais. Não estou dizendo que não é verdade. Apenas erra o ponto. O que a compilação gera do seu código não tem nada a ver com práticas de codificação e erros que você escreve
3
As perguntas eram "Está usando a ELSE programação ruim?". Minha resposta é: você pode fingir que não está lá, mas não evitá-lo.
precisa saber é o seguinte
5
que lingua ? em Java o else não está no bytecode: P
IAdapter
@ acidzombie24 - é uma prática muito boa considerar o que o 'mais' é em qualquer questão. Às vezes é apenas - não everyhtign outro lugar do fucntion, mas mostra que você pensou sobre isso
Martin Beckett
14

Pessoalmente, costumo evitar elseo máximo que posso, mas não é para qualquer problema de segurança .

Ao ler o código, instruções aninhadas tornam mais difícil seguir a lógica, porque você precisa lembrar qual conjunto de condições levará até lá. Por esse motivo, sou um grande fã de saída antecipada:

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

letThemIn();

Isto também se aplica fore whilelaços em que vou usar continuee breaksempre que evita um nível de recuo.

Chris Lattner diz isso melhor do que eu nos padrões de codificação LLVM .

Matthieu M.
fonte
Concordo. "else" cria um "garfo" mental e nós humanos somos criaturas seqüenciais.
user187291
FYI, ele é chamado de uma condição de guarda
CaffGeek
@Chad: ah graças, sempre bom ter nome para as coisas :)
Matthieu M.
1
+1. Esta é a única resposta que posso suportar que tenha uma pontuação> 1. *writes an answer*
Não tento evitar outra coisa como tal, mas acho que concordo com o que você escreve. O ponto é que o que você está testando deve ser a exceção e não o caso normal ( stackoverflow.com/questions/114342/… ). Aqui a falha na autenticação é a exceção e (somente) que deve ser testada.
hlovdal
5

Em seguida, basta substituí-los,

If (passwordCheck == true)
{
     letThemIn();
}
else
{
     displayMessage();
}
Ahmet Kakıcı
fonte
21
Que tal If ((passwordCheck == true) == true)? :-)
Hipopótamo
1
Bem, é o meu estilo, para melhorar a legibilidade. Você pode escrever if (! Value), mas eu prefiro if (value! = True) #
Ahmet Kakıcı
7
Não melhora a legibilidade. Apenas adiciona ruído. Pior ainda são os programadores que escrevem construções aninhadas se apenas porque têm medo dos operadores booleanos.
AK2
3
Se o nome da variável for descritivo, não há razão para isso: se (someBooleanValue == true) for necessário. Isso seria melhor: if (validPassword) {...
Mark Freedman
Bem, eu acabei de substituir os blocos de código na pergunta. Se você leu meu primeiro comentário, eu disse que prefiro usar if (value! = True) em vez de if (! Value). Espero que você possa ver a diferença entre if (value) e if (! Value). Eu quis dizer que não uso operador de complemento. Caso contrário, você está certo, verdadeiro significa verdadeiro.
Ahmet Kakıcı
3

Como Matthieu M., prefiro a saída antecipada a outros blocos profundamente aninhados ... Ilustra uma programação bem defensiva (se más condições, não há motivo para continuar). Muitas pessoas discordam de nós, preferindo um ponto de saída único; não é o objetivo do debate (eu acho).

Agora, eu certamente uso elsequando faz sentido, particularmente para alternativas simples e curtas. Como dito, duplicar o teste é uma perda de tempo (programadores e CPU), uma fonte de confusão e, posteriormente, de bugs (quando um é alterado, não o outro).
Em algum momento, adiciono um comentário à elsepeça, lembrando qual era a condição (principalmente se a ifpeça é longa, por exemplo, no código legado) ou qual é a alternativa.

Observe que alguns defensores extremos da programação funcional se propõem a se livrar inteiramente if, em favor da correspondência de padrões ... Um pouco extremo demais para o meu gosto. :-)

PhiLho
fonte
1
como uma observação lateral: se você tem uma construção if na sua linguagem funcional pura, precisa realmente ter outra. Toda expressão tem que retornar algo!
tokland
2

Não há nada de errado em usar o ELSE. No entanto, pode levar a códigos excessivamente complexos que são difíceis de ler e entender. Isso pode indicar um design incorreto. Certamente indica casos de uso adicionais que precisarão ser testados.

Tente remover os ELSEs, se puder - mas não fique paranóico com isso. Steve McConnell chama esse código de linha reta no Code Complete. Ou seja, existe um caminho claro e simples através do seu código.

Abordagens para tentar o seu problema específico:

  • use polimorfismo. No limite do seu sistema, valide as credenciais de segurança do usuário. Se eles são legítimos, retorne um objeto de sessão - com acesso às partes relevantes do sistema ou lance uma exceção. No entanto, isso pode tornar seu sistema mais complexo. Então você decide o que é mais fácil de entender e manter.

Em geral - o seguinte pode ajudar a reduzir os ELSEs no seu código:

  • bons requisitos podem reduzir a necessidade de tais decisões no código. Talvez você não precise implementar o caso de uso (mais).
  • design mais claro. Coesão máxima e minimização do acoplamento. Isso garante que os componentes não estejam duplicando as decisões tomadas em outros componentes.
  • tratamento de exceções para gerenciar casos de erro.
  • polimorfismo (veja o exemplo acima).
  • declarações de troca - essas são ELSEs glorificadas, mas são melhores em determinadas situações.
Conor
fonte
2
Eu também incluiria "Mover cheques booleanos complexos para uma função separada".
gablin
2

Sua suposição de que o código é um vazamento de segurança pode ou não ser verdadeira dependendo do idioma que você está usando. No código C, pode ser um problema (principalmente porque em C um booleano é apenas um int que não é zero ou zero) - mas nas linguagens mais fortemente tipadas (ou seja, verificação do tipo de tempo de execução) se a passwordCheckvariável foi declarada como booleana, não há como atribuir outra coisa a ele. De fato, tudo em um ifpredicado deve resolver para um booleano, se você usa os operadores booleanos ou simplesmente usa o valor. Se você conseguisse ter outro tipo de objeto vinculado ao passwordChecktempo de execução, lançaria algum tipo de exceção de conversão ilegal.

Construções simples if / else são muito mais fáceis de ler do que construções if / if - e menos propensas a problemas inadvertidos se alguém tentar inverter a construção. Vamos dar o mesmo exemplo por um segundo:

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

if(passwordCheck) {
    letThemIn();
}

O significado das cláusulas mutuamente exclusivas que você deseja executar acima está perdido. É isso que a construção if / else transmite. Dois ramos de execução mutuamente exclusivos, onde um deles sempre será executado. Essa é uma parte importante da segurança - garantindo que não haja comoletThemIn fazê-lo após a ligação denyAccess.

Para fins de clareza do código e para garantir que as seções críticas sejam mais protegidas, elas devem estar dentro da cláusula primária (a ifparte). O comportamento padrão não conforme deve estar na cláusula alternativa (a elseparte). Por exemplo:

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

NOTA: ao trabalhar com idiomas diferentes, desenvolvi um habbit de codificação que ajuda a evitar a pergunta "e se for uma string?" Essencialmente, é colocar a constante em primeiro lugar na expressão booleana. Por exemplo, em vez de verificar passwordCheck == false, estou verificando false == passwordCheck. Isso também evita o problema de atribuição acidental possível em C ++. Usando essa abordagem, o compilador reclamará se eu digitar= vez de ==. Em linguagens como Java e C #, o compilador trataria a atribuição na cláusula if como um erro, mas o C ++ aceitará com satisfação. É por isso que também costumo fazer verificação nula com a nullprimeira.

Se você muda rotineiramente os idiomas, colocar a constante em primeiro lugar é muito útil. No entanto, na minha equipe, é oposto ao padrão de codificação e o compilador captura esses problemas de qualquer maneira. Pode ser um hábito difícil de quebrar.

Berin Loritsch
fonte
1

Dizer que usar elsequando programar é ruim é como dizer que usar otherwisequando falar é ruim.

Claro, os dois podem ser usados ​​de maneiras ruins, mas isso não significa que eles devam ser evitados apenas porque você cometeu um erro que os incluiu. Eu não ficaria surpreso se muitos erros dependessem de um defaultcaso ausente em uma switchdeclaração.

gablin
fonte
1

Pense Elsena lista branca de seu fluxo de aplicativos. Você verifica as condições que DEVEM permitir que o fluxo do aplicativo continue, e se elas não forem atendidas, o seu Elseserá executado para resolver o problema, interromper a execução do aplicativo ou algo semelhante.

Else por si só não é ruim, mas se você o usar mal, poderá ver efeitos indesejados.

Além disso, em relação à sua declaração sobre

"Eu sei que o passwordCheck provavelmente será um booleano, mas eu não colocaria a segurança dos meus aplicativos nele."

Para métodos desenvolvidos, SEMPRE retorne um tipo de dados. Embora o PHP Core esteja repleto de códigos que retornam dois ou mais tipos de dados, essa é uma prática ruim, pois adivinha as chamadas de função. Se você precisar retornar mais de um tipo de dados, considere lançar uma exceção (acho que esse é o motivo pelo qual eu gostaria de retornar outro tipo de dados - algo deu horrivelmente, muito errado) ou considere reestruturar seu código para que você possa retorna apenas um tipo de dados.

Craige
fonte
Eu não sabia que havia idiomas que retornavam mais de um tipo de dados! Você está se referindo a funções polimórficas? Acredito que sempre que sobrecarrego uma função, ela sempre retorna o mesmo tipo de dados, embora possa levar argumentos diferentes.
Michael K
1
Em linguagens de tipo fraco, e especialmente no PHP, as funções podem retornar mais de um tipo de dados. por exemplo: stristr- "Retorna a substring correspondente. Se a agulha não for encontrada, retorna FALSE"
Craige
@ Michael, uma função PHP pode retornar o que você quiser. Não há restrição para o tipo de dados. Minha função mais complicada retorna verdadeiro / falso / nulo, mas não há nada (exceto o bom senso) que impede você de escrever uma função que retorna verdadeiro / inteiro / nulo / string / float / array.
TRiG
Interessante. Eu nunca trabalhei com PHP. Obrigado pela explicação, porém - provavelmente me ajude no futuro! tipo como Javascript então?
Michael K
@ Michael - Muito semelhante ao Javascript, de fato.
Craige
1

Em primeiro lugar. RI MUITO! Não há motivo para evitar mais nada. NÃO é uma má prática, seja de que forma for.

Se alguma coisa o código deve ser

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

Não há dois ifs lá e ele não tem mais. É isso que faço em todos os meus aplicativos, exceto um no qual lancei uma exceção. A exceção é capturada na minha função, que verifica o URL da página apropriada a ser exibida (ou, alternativamente, eu posso colocar a função catch / check na função de erro asp.net). Ele imprime uma página genérica que diz não autorizar ou qualquer mensagem que eu use na exceção (eu sempre verifico o tipo de exceção e defino o código de status http).

-Editar- como mostrado no exemplo de muniçãoQ dois ifs é ridículo. Realmente o resto é tão bom ou melhor que um if. Se alguma coisa for evitada (embora eu pessoalmente não. Mas eu uso muito retorno e quebrei), como já foi dito, mais caminhos de código aumentam a probabilidade de erros. Vejo Complexidade Ciclomática

-Editar 2- Se você está preocupado com o uso if / else. Também observarei que minha preferência é colocar o bloco de código mais curto no topo, como

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

Pelo contrário,

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

fonte
0

Eu gosto de definir um padrão antes das condicionais quando posso. Sinto que é um pouco mais fácil de ler e um pouco mais explícito, mas isso é apenas uma preferência. Eu tenho uma tendência a tentar evitar condições negativas no meu código. Não sou muito fã de procurar! Foo ou false == foo e sinto que o resto é meio equivalente condicional a um negativo.

foo = bar;

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

ao invés de ...

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

O bloco de código anterior parece um pouco mais fácil para eu ler. Parece-me mais natural ter uma espécie de paranóia cética sobre o meu código. Definir um padrão independentemente de qualquer condição me faz sentir confortável: P


fonte
0

Eu argumentaria que o uso de lógica de ramificação de qualquer tipo deve ser evitado o máximo possível. Embora nada esteja errado com ELSE ou IF, existem várias maneiras de escrever código para minimizar a necessidade de usar qualquer lógica de ramificação. Não estou dizendo que a lógica de ramificação possa ser totalmente eliminada - será necessária em alguns lugares - mas é possível refatorar o código para eliminar uma boa parte dela. Na maioria dos casos, isso melhorará a inteligibilidade e a precisão do seu código.

Como exemplo, os operadores ternários também são geralmente bons candidatos:

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

Usando uma abordagem ternária:

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

Os operadores ternários deslocam a ramificação para a direita em um bom caminho.

Mario T. Lanza
fonte