¿Deberían las declaraciones if / else ser organizadas por la rareza de los casos o la dificultad de tratar con ellos?

18

En algún código que estoy escribiendo ahora, tengo algo como esto:

if (uncommon_condition) {
    do_something_simple();
} else {
    do();
    something();
    long();
    and();
    complicated();
}

Una parte de mí piensa: "Está bien la forma en que está escrito. Los casos simples deberían ir primero y los casos más complicados deberían ir después". Pero otra parte dice: "¡No! El elsecódigo debe ir debajo de if, porque ifes para tratar casos inusuales y elsees para tratar todos los demás casos". ¿Cuál es correcto o preferible?

EMBLEMA
fuente
44
¡Ordene por simplicidad / comprensión de las condiciones! El resto puede ser atendido por optimizadores, predictores de rama y refactorización.
Marjan Venema
Es por eso que nos pagan mucho dinero: para tomar estas decisiones difíciles.

Respuestas:

24

Orden por su probabilidad de ser ejecutado. La (s) condición (es) más común (s), más probable (s) debe ir primero.

La "dificultad de tratar con ellos" debe abordarse mediante la estructura del código, la abstracción, etc. En el ejemplo, el bloque else podría refactorizarse a la llamada a un solo método. Desea que sus ifcláusulas estén en el mismo nivel abstracto.

if ( ! LotteryWinner ) {
    GoToWorkMonday();
} else {
    PlanYearLongVacation();
}
radarbob
fuente
66
Para el rendimiento, podría estar de acuerdo. Pero, de nuevo, la mayoría de los optimizadores y predictores de rama son perfectamente capaces de encargarse de eso. Para facilitar la lectura, podría estar de acuerdo si el caso más probable tiene significativamente más líneas que el caso menos probable. Pero para mí eso indicaría antes la necesidad de extraer un método que usar un not. Personalmente me concentraría en evitar las notcondiciones. Se ha demostrado que inducen una mayor carga cognitiva para comprender que las condiciones "positivas" y son perjudiciales para la legibilidad / comprensión del código. Tiendo a usarlos solo en declaraciones de guardia.
Marjan Venema
2
@ MarjanVenema, puedo ver tu punto sobre "no". Pero el doble negativo es el verdadero "no" WTF. El otro día me encontré con esto en nuestro código doesNotAllowxxFiles = false. Lo suficientemente malo, pero luego haz estoif(! doesNotAllowxxFiles)
radarbob
Sí, negativos dobles, negativos combinados con and's y / o or's (¡sic!) Y negativos combinados con un método / var con Not in the name, siempre me da vueltas el cerebro :-)
Marjan Venema
Si tiene problemas para comprender lo que está sucediendo, creo que a menudo es útil hacer cosas como: simpleBool = (! (complexBool || randomfFlag)) &&! randomFlag
TruthOf42
2
Estoy de acuerdo con usted, excepto por la existencia de Cláusulas de Guardia (que siempre tienen la condición excepcional en la ENTONCES porción de la declaración IF, no la condición común).
Robert Harvey
5

Intenta mejorar la legibilidad. Una forma es colocar el bloque de código más largo en la parte else.

if (s == null)
     // short code
else 
     // long 
     // code
     // block

es más legible que

if (s != null)
    // long
    // code
    // block
else
    // short code
CWallach
fuente
2
¿Por qué es más legible? No veo diferencia entre los dos en términos de legibilidad
John Demetriou
2
@JohnDemetriou El otro es más visible si el Entonces es corto. La gente primero busca la estructura y luego le asigna un significado. El código sin un Else es diferente del código con uno, por lo que hacer que el Else sea más visible es más comprensible. (Todas las demás cosas son iguales, los martes cuando no llueve, etc.)
4

Ninguna regla fija como tal escuché sobre el uso, pero sigo así

if(usual)
{
(more often)
}
else (unusual)
{
(rarely occurring)
}

Pero si ambos tienen la misma función con propiedades diferentes, entonces mejor optar por lo inusual primero que lo habitual para que pueda guardar una instrucción.


if(x == 0)  // 1
  {x = 1;}  // 2
else
  {x = 2;}  // 3

Para el código de ensamblaje de código anterior será algo como esto:

1. 000d 837DFC00        cmpl    $0, -4(%ebp)
   0011 7509            jne .L2

2. 0013 C745FC01        movl    $1, -4(%ebp)
   001a EB07            jmp .L3

    .L2:
3.001c C745FC02         movl    $2, -4(%ebp)

        .L3:

Si la condición interna es verdadera, entonces el flujo es 1-> 2 (4 instrucciones)
SI la condición interna es falsa, entonces el flujo es 1-> 3 (3 instrucciones)

Por lo tanto, es mejor poner eventos inusuales o que ocurren raramente en una condición parcial y normal en otra cosa para que podamos guardar una instrucción cada vez ;-)

Santosh Mudhol
fuente
2

He descubierto que el patrón opuesto exacto conduce a un código más fácil de leer y reduce o elimina las instrucciones anidadas if. Me refiero a esto como un patrón de "guantelete". (En la narración de cuentos, un guante sería una serie de desafíos que deben cumplirse con éxito antes de que se complete la tarea final). Al manejar sus casos límite primero , permite que el cuerpo principal de su código sea limpio y conciso:

if(gauntlet_1){ handle the first gauntlet condition }; 
if(gauntlet_2){ handle the second gauntlet condition };
...
// All preconditions (gauntlets) have been successfully handled

// Perform your main task here
Byron Jones
fuente
Por lo tanto, la parte de "manejar guantelete" tendría que ser una transferencia de control, como una declaración de lanzamiento o devolución, o tendría que corregir la falla. Algunas personas fruncen el ceño ante una serie de declaraciones if ... return, pero no lo hago y creo que de hecho hace que el código sea más fácil de leer, como dijiste.
1
Si la condición de guantelete es algo que evitaría que la función tenga éxito, devuelve un error inmediatamente. Si se puede manejar la condición de guantelete, lo hago antes de ingresar el cuerpo principal del código siempre que sea posible. Principalmente estoy tratando de evitar declaraciones IF anidadas, que en mi experiencia son una de las principales causas de errores de codificación oscuros y difíciles de depurar.
Byron Jones
0

Para mí, se trata más de las condiciones que de la complejidad del código que ejecutan. Debe ordenar las condiciones para atrapar primero las condiciones inusuales, luego las más comunes después. Además, si el código else es realmente largo y complicado, probablemente haría un sub para eso, para mantener la parte condicional obvia para el lector.

Steve Thomas
fuente
-1

No tengo una regla fija, pero generalmente sigo esto: primero, considere dónde hay un patrón de diseño perdido que lo tendrá en cuenta. Si no, lo escribo para que la condición en el if sea la más clara. Es decir, evitar los dobles negativos y similares.

Don Branson
fuente