Melhor maneira de formatar a instrução if com várias condições

88

Se você deseja que algum código seja executado com base em duas ou mais condições, qual é a melhor maneira de formatar essa instrução if?

primeiro exemplo: -

if(ConditionOne && ConditionTwo && ConditionThree)
{
   Code to execute
}

Segundo exemplo: -

if(ConditionOne)
{
   if(ConditionTwo )
   {
     if(ConditionThree)
     {
       Code to execute
     }
   }
}

que é mais fácil de entender e ler, tendo em mente que cada condição pode ser um nome de função longo ou algo assim.

Guy Coder
fonte
É uma pena que ninguém nesta página mencione a substring "rápido" ou "desempenho". Foi isso que vim aprender aqui.
Presidente Dreamspace

Respostas:

132

Eu prefiro a Opção A

bool a, b, c;

if( a && b && c )
{
   //This is neat & readable
}

Se você tiver variáveis ​​/ condições de método particularmente longas, você pode simplesmente quebrá-las

if( VeryLongConditionMethod(a) &&
    VeryLongConditionMethod(b) &&
    VeryLongConditionMethod(c))
{
   //This is still readable
}

Se eles forem ainda mais complicados, então eu consideraria fazer os métodos de condição separadamente fora da instrução if

bool aa = FirstVeryLongConditionMethod(a) && SecondVeryLongConditionMethod(a);
bool bb = FirstVeryLongConditionMethod(b) && SecondVeryLongConditionMethod(b);
bool cc = FirstVeryLongConditionMethod(c) && SecondVeryLongConditionMethod(c);

if( aa && bb && cc)
{
   //This is again neat & readable
   //although you probably need to sanity check your method names ;)
}

IMHO A única razão para a opção 'B' seria se você tivesse elsefunções separadas para executar para cada condição.

por exemplo

if( a )
{
    if( b )
    {
    }
    else
    {
        //Do Something Else B
    }
}
else
{
   //Do Something Else A
}
Eoin Campbell
fonte
Eu gosto disso. Embora eu não seja um grande fã da ideia do método, a menos que os métodos já existam e retornem um valor booleano.
Thomas Owens
2
Você não está avaliando tudo sem motivo no segundo exemplo?
Odys
Eu usaria o '&&' antes da condição. É difícil ter condições com o mesmo comprimento de caracteres do exemplo.
Darkov
28

Outras respostas explicam por que a primeira opção normalmente é a melhor. Mas se você tiver várias condições, considere criar uma função (ou propriedade) separada fazendo as verificações de condição na opção 1. Isso torna o código muito mais fácil de ler, pelo menos quando você usa bons nomes de método.

if(MyChecksAreOk()) { Code to execute }

...

private bool MyChecksAreOk()
{ 
    return ConditionOne && ConditionTwo && ConditionThree;
}

Se as condições dependem apenas de variáveis ​​de escopo local, você pode tornar a nova função estática e passar tudo o que precisa. Se houver mistura, passe nas coisas locais.

Torbjørn
fonte
2
Descobri que este é o mais eficaz e mais fácil de adicionar condições mais tarde
pbojinov
1 no início eu levantei uma sobrancelha, mas esta realmente é a melhor resposta imho. ter esse booleano isOkToDoWhatevercomo uma propriedade faz muito sentido.
grinch
1
Mas isso apenas move a mesma condição complexa para outro lugar onde também precisa ser legível, então estamos de volta à estaca zero com isso. Não se trata apenas de iflegibilidade de instrução, mas sim de legibilidade de condição.
Robert Koritnik
@RobertKoritnik Eu entendo o que você está dizendo, mas não acho que voltamos à estaca zero porque reduzimos a complexidade que o leitor precisa considerar de uma vez. Ela pode olhar para as condições OU ela pode olhar para o código usando as condições em que as condições têm um (espero) nome legal. Pelo menos frequentemente acho isso mais fácil de grocar, mas, novamente, às vezes é bom ter todos os detalhes em um só lugar. Como sempre, depende.
Torbjørn
Excelente ponto de utilizar bons nomes de métodos e refatoração de lógica.
JB Lovell
10

O primeiro exemplo é mais "fácil de ler".

Na verdade, na minha opinião, você só deve usar o segundo sempre que tiver que adicionar alguma "lógica else", mas para um Condicional simples, use o primeiro tipo. Se você está preocupado com a extensão da condição, você sempre pode usar a próxima sintaxe:

if(ConditionOneThatIsTooLongAndProbablyWillUseAlmostOneLine
                 && ConditionTwoThatIsLongAsWell
                 && ConditionThreeThatAlsoIsLong) { 
     //Code to execute 
}

Boa sorte!

David Santamaria
fonte
9

A pergunta foi feita e, até agora, foi respondida como se a decisão devesse ser tomada puramente por motivos "sintáticos".

Eu diria que a resposta certa de como você define uma série de condições dentro de um if também deve depender da "semântica". Portanto, as condições devem ser divididas e agrupadas de acordo com o que as coisas vão juntas "conceitualmente".

Se dois testes são realmente dois lados da mesma moeda, por exemplo. if (x> 0) && (x <= 100) então coloque-os juntos na mesma linha. Se outra condição é conceitualmente muito mais distante, por exemplo. user.hasPermission (Admin ()) e colocá-lo em sua própria linha

Por exemplo.

if user.hasPermission(Admin()) {
   if (x >= 0) && (x < 100) {
      // do something
   }
}
interstar
fonte
7
if (   ( single conditional expression A )
    && ( single conditional expression B )
    && ( single conditional expression C )
   )
{
   opAllABC();
}
else
{
   opNoneABC();
}

Formatando várias expressões condicionais em uma instrução if-else desta forma:

  1. permite maior legibilidade:
    a. todas as operações lógicas binárias {&&, ||} na expressão mostrada primeiro
    b. ambos os operandos condicionais de cada operação binária são óbvios porque eles se alinham verticalmente
    c. operações de expressões lógicas aninhadas tornam-se óbvias usando recuo, assim como instruções de aninhamento dentro da cláusula
  2. requer parênteses explícitos (não confie nas regras de precedência do operador)
    a. isso evita erros comuns de análise estática
  3. permite uma depuração mais fácil
    a. desabilite testes condicionais únicos individuais com apenas a //
    b. definir um ponto de interrupção antes ou depois de qualquer
    ceg de teste individual ...
// disable any single conditional test with just a pre-pended '//'
// set a break point before any individual test
// syntax '(1 &&' and '(0 ||' usually never creates any real code
if (   1
    && ( single conditional expression A )
    && ( single conditional expression B )
    && (   0
        || ( single conditional expression C )
        || ( single conditional expression D )
       )
   )
{
   ... ;
}

else
{
   ... ;
}
Sean
fonte
Este é o meu método. O único problema que tenho é que ainda estou para encontrar um embelezador de código que ofereça isso como uma opção
Speed8ump
4

O segundo é um exemplo clássico do Anti-padrão de Flecha, então eu o evito ...

Se suas condições forem muito longas, extraia-as em métodos / propriedades.

Omar Kooheji
fonte
3

A primeira é mais fácil, porque se você ler da esquerda para a direita terá: "Se algo E algo mais E algo mais ENTÃO", que é uma frase fácil de entender. O segundo exemplo diz "Se algo ENTÃO se algoelse ENTÃO se algo mais ENTÃO", o que é desajeitado.

Além disso, considere se você quisesse usar algumas RUP em sua cláusula - como você faria isso no segundo estilo?

RB.
fonte
1

Eu acredito que a switch...casedeclaração é a melhor maneira de escrever um código organizado sob esta circunstância, se a linguagem de programação suportar.

switch (//variable or Boolean) {
  case //Condition A:
  case //Condition B:
  case //Condition C:
    //Code to execute;
}
Kent
fonte
Pode ser bom, mas remove a "vantagem" da avaliação de "curto-circuito" / preguiçosa.
TheLibrarian de
Esta resposta está completamente errada e realmente perigosa. A questão é sobre uma condição conjuntiva. As instruções switch são apenas uma solução para condições disjuntivas.
egyik
0

Em Perl, você pode fazer isso:

{
  ( VeryLongCondition_1 ) or last;
  ( VeryLongCondition_2 ) or last;
  ( VeryLongCondition_3 ) or last;
  ( VeryLongCondition_4 ) or last;
  ( VeryLongCondition_5 ) or last;
  ( VeryLongCondition_6 ) or last;

  # Guarded code goes here
}

Se alguma das condições falhar, ele simplesmente continuará, após o bloqueio. Se você estiver definindo quaisquer variáveis ​​que deseja manter após o bloco, precisará defini-las antes do bloco.

Brad Gilbert
fonte
1
Isso parece Perlish - no "isso faz O QUÊ?" sentido;) Mas é realmente legível, uma vez que você se acostuma.
Piskvor saiu do prédio de
-2

Há muito tempo que enfrento esse dilema e ainda não consigo encontrar uma solução adequada. Na minha opinião, a única boa maneira é primeiro tentar se livrar das doenças antes, para que você não compare 5 delas de repente.

Se não houver alternativa, então, como outros sugeriram - divida em partes separadas e encurte os nomes ou agrupe-os e, por exemplo, se todos devem ser verdadeiros, use algo como "se não for falso no array de x, execute".

Se tudo falhar, @Eoin Campbell deu ideias muito boas.

Tiper Loc
fonte
Isso não está adicionando nada de novo às respostas já existentes.
jerney
-4

Quando a condição é realmente complexa, eu uso o seguinte estilo (exemplo da vida real do PHP):

if( $format_bool &&
    (
        ( isset( $column_info['native_type'] )
            && stripos( $column_info['native_type'], 'bool' ) !== false
        )
        || ( isset( $column_info['driver:decl_type'] )
            && stripos( $column_info['driver:decl_type'], 'bool' ) !== false
        )
        || ( isset( $column_info['pdo_type'] )
            && $column_info['pdo_type'] == PDO::PARAM_BOOL
        )
    )
)

Acredito que seja mais agradável e legível do que aninhar vários níveis de if(). E em alguns casos como este, você simplesmente não pode quebrar uma condição complexa em partes, porque do contrário você teria que repetir as mesmas instruções em if() {...}bloco muitas vezes.

Também acredito que adicionar um pouco de "ar" ao código é sempre uma boa ideia. Melhora muito a legibilidade.

CodeDriller
fonte