Pregunta

Si tengo una función con un montón de condicionales, ¿cuál es la mejor manera de organizarla?

Lo que me preocupa es que alguien más ingrese al código y entienda lo que está sucediendo. Aunque el ejemplo es simple, imagine que el condicional es muy complejo.

Para un ejemplo:

public void function(string value, string value2)
{
    if (value == null)
        return;

    if (value2 == value)
        DoSomething();
}

o

public void function(string value, string value2)
{
    if (value != null)
    {
        if (value2 == value)
            DoSomething();
    }
}

o

public void function(string value, string value2)
{
    if (value != null && value2 == value)
        DoSomething();
}
¿Fue útil?

Solución

Organice las condiciones y póngalas en un método.

por ejemplo, reemplace esto:

 if( a& & n || c  && ( ! d || e ) && f > 1 && ! e < xyz ) { 
      // good! planets are aligned.
      buyLotteryTicket();
 } else if( ..... oh my ... ) { 
 }

En esto:

if( arePlanetsAligned() ) { 
    buyLotteryTicket(); 
} else if( otherMethodHere() ) { 
   somethingElse();
}  

De esa manera, realmente no importa qué estilo use (1, 2 o 3) porque la declaración if describirá claramente cuál es la condición que se está probando. No hay necesidad de construcciones adicionales.

El objetivo es hacer el código más claro y autodocumentado. Si está utilizando un lenguaje de programación OO, puede usar un objeto para almacenar el estado (variables) y evitar crear métodos que tomen de 5 a 10 parámetros.

Estas son preguntas similares:

La mejor manera de deshacerse de los if anidados

¿Hay una alternativa a este código hiperidentado

El segundo enlace muestra una forma más completa y compleja de transformar una horrible pesadilla para todos en un código autodocumentado.

Muestra cómo transformar esto:

public String myFunc(SomeClass input)
{
    Object output = null;

    if(input != null)
    {
        SomeClass2 obj2 = input.getSomeClass2();
        if(obj2 != null)
        {
            SomeClass3 obj3 = obj2.getSomeClass3();
            if(obj3 != null && !BAD_OBJECT.equals(obj3.getSomeProperty()))
            {
                SomeClass4 = obj3.getSomeClass4();
                if(obj4 != null)
                {
                    int myVal = obj4.getSomeValue();
                    if(BAD_VALUE != myVal)
                    {
                        String message = this.getMessage(myVal);
                        if(MIN_VALUE <= message.length() &&
                           message.length() <= MAX_VALUE)
                        {
                            //now actually do stuff!
                            message = result_of_stuff_actually_done;
                        }
                    }
                }
            }
        }
    }
    return output;
}

en esto:

if ( isValidInput() && 
    isRuleTwoReady() &&
    isRuleTreeDifferentOf( BAD_OBJECT ) &&
    isRuleFourDifferentOf( BAD_VALUE ) && 
    isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) { 
            message = resultOfStuffActuallyDone();
}

Otros consejos

Prefiero la primera opción: falla rápido es más limpio, más claro y más fácil leer y entender.

Entiendo que esto no es un fracaso, pero el concepto aún se aplica. Realmente no me gustan las declaraciones anidadas de if .

Puede mirar la programación defensiva para garantizar que se pueda cumplir el contrato para la funcionalidad de los métodos.

public void function(string value, string value2)
{
    if (string.IsNullOrEmpty(value1)) throw new ArgumentNullException("value1", "value 1 was not set");
    if (string.IsNullOrEmpty(value2)) throw new ArgumentNullException("value2", "value 2 was not set");

    DoSomething();
}

Refactorice esas cosas en su propia función. Es mucho mejor leer un nombre de función descriptivo que un montón de expresiones booleanas.

// leave complex conditional code out, so that we can focus on the larger problem in the function
public void function(string value, string value2)
{
    if (MyDescriptiveTestName)
    {
        DoSomething();
    }
}

// encapsulate complex conditional code so that we can focus solely on it.
private bool MyDescriptiveTestName(string value, string value2)
{
    if (value != null && value2 == value)
    {
        return true;
    }
    return false;
}

¿Puedo recomendar el libro Clean Code de Robert C. Martin que proporciona un gran conjunto de heurísticas para escribir código legible y mantenible.

Ahora, otra opción sería extraer el condicional en otra función privada y nombrarlo para que describa su intención. No funciona muy bien con el código suministrado, ya que es genérico, pero se vería algo así como:

public void function(string value, string value2)
{
    if (valuesAreValidAndEqual(value, value2))
    {
        DoSomething();
    }
}

private void valuesAreValidAndEqual(string value, string value2)
{
    return value != null && value2 == value;
}

Claramente, esto es más útil si los nombres de las variables y los nombres de las funciones están relacionados con su dominio.

Si tiene una función con muchos condicionales, usaría una instrucción switch , no ifs. También podría dividir los detalles en varias funciones (o incluso clases) si eso es posible.

Artículos relacionados de desbordamiento de pila :

Me gusta la tercera opción, pero esto realmente depende del idioma. Está asumiendo que en el tercero la primera parte de la declaración fallará y no ejecutará la segunda. Esto depende del idioma. Sé que la mayoría de los lenguajes basados ??en C hacen esto, pero dado que no especificó uno que sea un problema potencial. Podría haber una que no conozco y que no tiene el concepto de cortocircuito.

El hecho de que esté pensando de antemano en la legibilidad del código es la mitad de la batalla.

En cuanto a cuál de sus ejemplos es más legible, eso es bastante subjetivo.

Mis pensamientos sobre los ejemplos dados:

  • Personalmente, creo que el primer ejemplo es más fácil de seguir.
  • Minimizando los niveles de anidación y / o cantidad de condicionales usualmente mejora la legibilidad.
  • Algunos argumentarían contra múltiples puntos de salida de un método (como ejemplo 1), pero creo que solo t se convierte en un problema cuando el método se pone muy largo. No es realmente un gran problema si solo eres comprobando entradas y fallando rápidamente.

Mi preferencia es la segunda opción. Personalmente, cuando leo un código como ese, tengo en cuenta las condiciones bajo las cuales ingresaría a cada nivel anidado. En el primer ejemplo, probablemente olvidaría que la primera condición (valor == nulo es falso) continúa manteniéndose. El tercero tampoco está mal, pero prefiero el segundo.

El hecho de que tenga tantas declaraciones en una función es una señal de que la función debe dividirse en otras más pequeñas.

No existe la MEJOR manera de colocar declaraciones si, creo que las respuestas son subjetivas.

La claridad es a menudo una cualidad difícil de juzgar. Lo que tiene claro cuando está escribiendo un código puede ser completamente obtuso para otra persona, o incluso para usted mismo después de que pase el tiempo suficiente.

Aquí están mis reglas generales personales cuando construyo una función con muchas verificaciones condicionales:

  1. Agrupe las condiciones en unidades lógicas, cuando sea posible
  2. Utilice valores intermedios claramente nombrados para simplificar la legibilidad y la depuración.
  3. Evite repetir las mismas construcciones lógicas más de una vez (ver arriba).
  4. Cuando sea posible, refactorice condiciones complejas o costosas en funciones separadas.
  5. Cuando sea posible, use las condiciones de salida temprana en la parte superior del método para salir, pero ...
  6. Evite las declaraciones de retorno de pimienta en todo el cuerpo del método.
  7. No confíe en la precedencia del operador, use paréntesis para agrupar las declaraciones.
  8. Evite confiar en la sangría, use bloques ({...}) incluso con una sola declaración, esto ayuda con la mantenibilidad.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top