Pregunta

Es algo que me molesta en todos los idiomas que he usado, tengo una declaración if pero la parte condicional tiene tantas comprobaciones que tengo que dividirla en varias líneas, usar una declaración if anidada o simplemente aceptar que es fea y seguir adelante. con mi vida.

¿Hay algún otro método que haya encontrado que pueda ser útil para mí y para cualquier otra persona que tenga el mismo problema?

Ejemplo, todo en una línea:

if (var1 = true && var2 = true && var2 = true && var3 = true && var4 = true && var5 = true && var6 = true)
{

Ejemplo, multilínea:

if (var1 = true && var2 = true && var2 = true
 && var3 = true && var4 = true && var5 = true
 && var6 = true)
{

Anidado de ejemplo:

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
¿Fue útil?

Solución

Separe la condición en varios valores booleanos y luego utilice un booleano maestro como condición.

bool isOpaque = object.Alpha == 1.0f;
bool isDrawable = object.CanDraw && object.Layer == currentLayer;
bool isHidden = hideList.Find(object);

bool isVisible = isOpaque && isDrawable && ! isHidden;

if(isVisible)
{
    // ...
}

Mejor todavía:

public bool IsVisible {
    get
    {
        bool isOpaque = object.Alpha == 1.0f;
        bool isDrawable = object.CanDraw && object.Layer == currentLayer;
        bool isHidden = hideList.Find(object);

        return isOpaque && isDrawable && ! isHidden;
    }
}

void Draw()
{
     if(IsVisible)
     {
         // ...
     }
}

Asegúrese de darle a sus variables nombres que realmente indiquen la intención en lugar de la función.Esto ayudará enormemente al desarrollador a mantener su código...¡podría ser usted!

Otros consejos

Me sorprende que nadie haya recibido este todavía.Existe una refactorización específica para este tipo de problema:

http://www.refactoring.com/catalog/decomposeConditional.html

Hay dos cuestiones que abordar aquí:legibilidad y comprensibilidad

La solución de "legibilidad" es una cuestión de estilo y, como tal, está abierta a interpretación.Mi preferencia es esta:

if (var1 == true && // Explanation of the check
    var2 == true && // Explanation of the check
    var3 == true && // Explanation of the check
    var4 == true && // Explanation of the check
    var5 == true && // Explanation of the check
    var6 == true)   // Explanation of the check
    { }

o esto:

if (var1 && // Explanation of the check
    var2 && // Explanation of the check
    var3 && // Explanation of the check
    var4 && // Explanation of the check
    var5 && // Explanation of the check
    var6)   // Explanation of the check
    { }

Dicho esto, este tipo de verificación compleja puede ser bastante difícil de analizar mentalmente mientras se escanea el código (especialmente si no eres el autor original).Considere la posibilidad de crear un método auxiliar para abstraer parte de la complejidad:

/// <Summary>
/// Tests whether all the conditions are appropriately met
/// </Summary>
private bool AreAllConditionsMet (
    bool var1,
    bool var2,
    bool var3,
    bool var4,
    bool var5,
    bool var6)
{
    return (
        var1 && // Explanation of the check
        var2 && // Explanation of the check
        var3 && // Explanation of the check
        var4 && // Explanation of the check
        var5 && // Explanation of the check
        var6);  // Explanation of the check
}

private void SomeMethod()
{
    // Do some stuff (including declare the required variables)
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6))
    {
        // Do something
    }
}

Ahora, al escanear visualmente el método "SomeMethod", la complejidad real de la lógica de la prueba queda oculta, pero el significado semántico se conserva para que los humanos lo entiendan en un alto nivel.Si el desarrollador realmente necesita comprender los detalles, se puede examinar el método AreAllConditionsMet.

Creo que esto se conoce formalmente como el patrón de refactorización "Descomponer condicional".¡Herramientas como Resharper o Refactor Pro!¡Puede facilitar este tipo de refactorización!

En todos los casos, la clave para tener un código legible y comprensible es utilizar nombres de variables realistas.Si bien entiendo que este es un ejemplo artificial, "var1", "var2", etc. no nombres de variables aceptables.Deben tener un nombre que refleje la naturaleza subyacente de los datos que representan.

A menudo los divido en variables booleanas de componentes:

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...

Primero, eliminaría todos los == true partes, eso lo haría un 50% más corto;)

Cuando tengo una gran condición busco las razones.A veces veo que debería usar polimorfismo, a veces necesito agregar algún objeto de estado.Básicamente, implica que se necesita una refactorización (un olor a código).

A veces uso Las leyes de De Morgan para simplificar un poco las expresiones booleanas.

Verificar Patrones de implementación por Kent Beck.Hay un patrón particular en el que estoy pensando que puede ayudar en esta situación...se llama "Guardias".En lugar de tener toneladas de condiciones, puedes dividirlas en una guardia, lo que deja en claro cuáles son las condiciones adversas en un método.

Entonces, por ejemplo, si tiene un método que hace algo, pero hay ciertas condiciones en las que no debería hacer algo, en lugar de:

public void doSomething() {
    if (condition1 && condition2 && condition3 && condition4) {
        // do something
    }
}

Podrías cambiarlo a:

public void doSomething() {
    if (!condition1) {
        return;
    }

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

Es un poco más detallado, pero mucho más legible, especialmente cuando comienzas a tener un anidamiento extraño, el guardia puede ayudar (combinado con métodos de extracción).

Por cierto, recomiendo ALTAMENTE ese libro.

He visto a muchas personas y editores sangrar cada condición en su declaración if con una pestaña o compararla con el par abierto:

if (var1 == true
    && var2 == true
    && var3 == true
   ) {
    /* do something.. */
}

Normalmente pongo el par cercano en la misma línea que la última condición:

if (var1 == true
    && var2 == true
    && var3 == true) {
    /* do something.. */
}

Pero no creo que esto sea tan limpio.

El consejo de Steve McConell, de Código completo:Utilice una tabla multidimensional.Cada variable sirve como un índice de la tabla, y la instrucción IF se convierte en una búsqueda de tabla.Por ejemplo, si (size == 3 && weight> 70) se traduce en la decisión de entrada de la tabla [size] [weight_group

Intente buscar functores y predicados.El proyecto Apache Commons tiene un gran conjunto de objetos que le permiten encapsular la lógica condicional en objetos.Un ejemplo de su uso está disponible en O'reilly. aquí.Extracto de ejemplo de código:

import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents = 
    ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );

Ahora los detalles de todos esos predicados isHonorRoll y los cierres utilizados para evaluarlos:

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student 
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return( ( s.getGrade().equals( "A" ) ) ||
            ( s.getGrade().equals( "B" ) && 
              s.getAttendance() == PERFECT ) );
  }
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return ( ( s.getGrade().equals( "D" ) || 
               s.getGrade().equals( "F" ) ) ||
             s.getStatus() == SUSPENDED );
  }
};

// Anonymous Closure that adds a student to the 
// honor roll
Closure addToHonorRoll = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Add an award to student record
    s.addAward( "honor roll", 2005 );
    Database.saveStudent( s );
  }
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Flag student for special attention
    s.addNote( "talk to student", 2005 );
    s.addNote( "meeting with parents", 2005 );
    Database.saveStudent( s );
  }
};

Bueno, primero que nada, ¿por qué no?

si (var1 && var2 && var2 && var3 && var4 && var5 && var6) {
...

Además, es muy difícil refactorizar ejemplos de código abstracto.Si mostrara un ejemplo específico, sería más fácil identificar un patrón mejor que se ajuste al problema.

No es mejor, pero lo que he hecho en el pasado:(El siguiente método evita cortocircuitos en las pruebas booleanas; todas las pruebas se ejecutan incluso si la primera es falsa.No es un patrón recomendado a menos que sepas que siempre necesitas ejecutar todo el código antes de regresar. ¡Gracias a ptomato por detectar mi error!)

booleano ok = cond1;
ok &= cond2;
ok &= cond3;
bien &= cond4;
ok &= cond5;
ok &= cond6;

Que es lo mismo que: (no es lo mismo, ¡vea la nota anterior!)

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

Recurro a valores booleanos separados:

Bool cond1 == (var1 && var2);
Bool cond2 == (var3 && var4);

if ( cond1 && cond2 ) {}

Como otros han mencionado, analizaría sus condicionales para ver si hay alguna manera de subcontratarlos a otros métodos para aumentar la legibilidad.

En lenguajes reflexivos como PHP, puedes usar variables-variables:

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }

Me gusta dividirlos por nivel, así que le daría formato a su ejemplo así:

if (var1 = true
 && var2 = true
 && var2 = true
 && var3 = true
 && var4 = true
 && var5 = true
 && var6 = true){

Es útil cuando tienes más anidamiento, como este (obviamente las condiciones reales serían más interesantes que "= verdadero" para todo):

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){

Si estás programando en Python, es muy fácil con el software incorporado all() función aplicada sobre la lista de sus variables (aquí solo usaré literales booleanos):

>>> L = [True, True, True, False, True]
>>> all(L) # True, only if all elements of L are True.
False
>>> any(L) # True, if any elements of L are True.
True

¿Existe alguna función correspondiente en su idioma (C#?¿Java?).Si es así, probablemente ese sea el enfoque más limpio.

McDowell,

Tiene razón en que cuando se utiliza el operador único '&', ambos lados de la expresión evalúan.Sin embargo, cuando se utiliza el operador '&&' (al menos en C#), la primera expresión que devuelve falso es la última expresión evaluada.Esto hace que poner la evaluación antes de la declaración FOR sea tan bueno como cualquier otra forma de hacerlo.

@tweakt

No es mejor, pero lo que he hecho en el pasado:

booleano ok = cond1;ok &= cond2;ok &= cond3;bien &= cond4;ok &= cond5;ok &= cond6;

Que es lo mismo que:

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

En realidad, estas dos cosas no son iguales en la mayoría de los idiomas.La segunda expresión normalmente dejará de evaluarse tan pronto como una de las condiciones sea falsa, lo que puede representar una gran mejora en el rendimiento si evaluar las condiciones es costoso.

Para facilitar la lectura, personalmente prefiero la propuesta anterior de Mike Stone.Es fácil comentar detalladamente y conserva todas las ventajas computacionales de poder salir temprano.También puede hacer la misma técnica en línea en una función si confunde la organización de su código para alejar la evaluación condicional de su otra función.Es un poco cursi, pero siempre puedes hacer algo como:

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);

el while (falso) es un poco cursi.Desearía que los idiomas tuvieran un operador de alcance llamado "una vez" o algo que se pudiera eliminar fácilmente.

Si lo estuviera haciendo en Perl, así es como podría ejecutar las comprobaciones.

{
  last unless $var1;
  last unless $var2;
  last unless $var3;
  last unless $var4;
  last unless $var5;
  last unless $var6;

  ... # Place Code Here
}

Si planea usar esto sobre una subrutina, reemplace cada instancia de last con return;

Me gusta dividir cada condición en variables descriptivas.

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid;
isVar1Valid = ( var1 == 1 )
isVar2Valid = ( var2.Count >= 2 )
isVar3Valid = ( var3 != null )
isVar4Valid = ( var4 != null && var4.IsEmpty() == false )
if ( isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid ) {
     //do code
}
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

Opuesto a

    if (condition_A) {
       if (condition_B) {
          if (condition_C) {
             if (condition_D) {
                if (condition_E) {
                   if (condition_F) {
                      ...
                   }
                }
             }
          }
       }
    }

y

    if (   (   (condition_A)
            && (condition_B)
           )
        || (   (condition_C)
            && (condition_D)
           )
        || (   (condition_E)
            && (condition_F)
           )
       )
    {
       do_this_same_thing();
    }

Opuesto a

    if (condition_A && condition_B) {
       do_this_same_thing();
    }
    if (condition_C && (condition_D) {
       do_this_same_thing();
    }
    if (condition_E && condition_F) {
       do_this_same_thing();
    }

La mayoría de las herramientas de análisis estático para examinar código se quejarán si múltiples expresiones condicionales no utilizan paréntesis explícitos que dictan el análisis de expresiones, en lugar de depender de reglas de precedencia de operadores y menos paréntesis.

La alineación vertical en el mismo nivel de sangría de llaves de apertura/cierre {}, paréntesis de apertura y cierre (), expresiones condicionales con paréntesis y operadores a la izquierda es una práctica muy útil, que MEJORA enormemente la legibilidad y claridad del código en lugar de bloquearlo todo. que posiblemente se pueda agrupar en una sola línea, sin alineación vertical, espacios o paréntesis

Las reglas de precedencia de operadores son complicadas, p.&& tiene mayor precedencia que ||, pero | tiene precedencia que &&

Entonces, ...

    if (expr_A & expr_B || expr_C | expr_D & expr_E || expr_E && expr_F & expr_G || expr_H {
    }

es una expresión condicional múltiple realmente fácil de leer y evaluar incorrectamente para simples humanos.

    if (   (  (expr_A)
            & (expr_B)
           )
        || (  (expr_C)
            | (  (expr_D)
               & (expr_E)
              )
           )
        || (   (expr_E)
            && (  (expr_F)
                & (expr_G)
               )
           )
        || (expr_H)
       )
    {
    }

No hay nada de malo en que el espacio horizontal (saltos de línea), la alineación vertical o los paréntesis explícitos guíen la evaluación de la expresión, todo lo cual MEJORA la legibilidad y la claridad.

Si haces esto:

if (var1 == true) {
    if (var2 == true) {
        if (var3 == true) {
            ...
        }
    }
}

Entonces también puedes responder a los casos en los que algo no es cierto.Por ejemplo, si está validando una entrada, podría darle al usuario un consejo sobre cómo formatearla correctamente, o lo que sea.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top