¿Cómo prevenir hendiduras profundas? [cerrado]

17

¿Qué pasos y medidas puedo tomar para evitar hendiduras profundas en mi código?

Tamara Wijsman
fuente
2
Mucha gente hablará sobre la refactorización aquí. Tal vez esto sea demasiado pedir, pero si publicaste un código (no demasiado largo) que está profundamente sangrado, y la gente podría mostrarte cómo lo refactorizarían. Por supuesto, que probablemente hace que el lenguaje específico pregunta entonces ...
Paddyslacker
3
Use un ancho de pestaña más pequeño.
mipadi
66
Punta de flecha antipatrón. Google, un montón de consejos
NimChimpsky
2
Dejar de usar python: D
back2dos
Es hora de ver su control y lógica de bucle. Probablemente su código sea más complicado de lo que debe ser, y una reconceptualización del problema conducirá a un código mucho más corto. Estudie un buen código y aprenda las técnicas.
Macneil

Respuestas:

14

La sangría profunda generalmente no es un problema si cada función / método en su programa hace una sola cosa. Ocasionalmente, puede ser necesario anidar condicionales a unos pocos niveles de profundidad, pero honestamente puedo decir que solo he escrito código con sangría profunda un puñado de veces en más de 12 años de codificación.

Chinmay Kanchi
fuente
26

Lo mejor que puedes hacer es extraer métodos:

int Step1(int state)
{
    if (state == 100)
    {
        return Step2(state);
    }
    else
    {
        return Step3(state);
    }
}

int Step2(int state)
{
    if (state != 100)
    {
        throw new InvalidStateException(2, state);
    }

    // ....
}
ChaosPandion
fuente
2
Esto también funciona para condiciones complejas if. Llevado al extremo, terminarás con un pseudocódigo ejecutable.
Alan Plum
Otra mejor cosa que podemos hacer es probablemente dejar caer elsebloques innecesarios .
Sepehr
16

¿Quizás podrías considerar cláusulas de guardia ?

en lugar de

public void DoSomething(int value){
    if (someCondition){
           if(someOtherCondition){
                if(yetAnotherCondition){
                       //Finally execute some code
                }
           }
    }
} 

Hacer

public void DoSomething(int value){
    if(!(someCondition && someOtherCondition && yetAnotherCondition)){
        return;
        //Maybe throw exception if all preconditions must be true
    }
    //All preconditions are safe execute code
}

Si alguna vez tienes la oportunidad, te recomiendo que leas Code Complete de Steve McConnell. Tiene muchos buenos consejos sobre estos temas.

http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=pd_sim_b_6

Para obtener más información sobre las "cláusulas de protección", consulte: https://sourcemaking.com/refactoring/replace-nested-conditional-with-guard-clauses

Jason Turan
fuente
8

Invierte tu ifs.

En lugar de:

if (foo != null)
{
    something;
    something;
    if (x)
    {        
       something;
    }
    something;
}
else
{
    boohoo;
}

Yo escribiría:

if (foo == null)
{
    boohoo;
    return;
}
something;
something;
if (x)
{        
   something;
}
something;

Lo mismo se aplica a if- elsebloques. Si elsees más corto / menos anidado, reviértalos.

Verifique los valores de los parámetros en un solo lugar

Verifique todos los parámetros de valores ilegales tan pronto como ingrese su método, luego continúe sabiendo que está a salvo. Crea un código más legible, pero también le ahorra acumular bloques condicionales más adelante y distribuir estas comprobaciones por toda la subrutina.

Konrad Morawski
fuente
1
¿Este estilo tiene un nombre específico?
Thomas Lauria
@ThomasLauria no que yo supiera. Es solo salir temprano. Ifs al comienzo del código que detiene el flujo de ejecución debido a que no se cumple alguna condición, también se conocen como cláusulas de salvaguarda , como señaló @JasonTuran. Y eso parece estar lo más cerca posible de tener un nombre distinto.
Konrad Morawski
Hace algunos años, mi supervisor me dijo que este estilo se llamaba "programación lineal", pero creo que fue un fantasma suyo;)
Thomas Lauria
4

Por lo general, he visto que el código con sangría profunda suele ser un código problemático. Si enfrenta este problema, retroceda y evalúe si su función está haciendo demasiadas cosas.

Al mismo tiempo, para responder a su pregunta, si existe la necesidad de una sangría tan profunda, sugeriría que la deje allí. Por la sencilla razón de que en dicho código, la sangría ayudará, ya que es probable que sea un código muy largo.

Vaibhav
fuente
2

Divida los componentes anidados (especialmente los repetidos) en funciones separadas (esto es más fácil si su idioma admite cierres) o reemplace una serie de bucles anidados con una recursividad.

Además, sangra dos espacios en lugar de cuatro.

Hoa Long Tam
fuente
55
Una vez que has ido al paso de cambiar el ancho de tu pestaña, estás en serios problemas ...
Daenyth
Las pestañas de dos espacios son lo difícil ...
David Thornley
66
Cambiar la sangría es solo una forma de ocultar el problema, no una solución
Murph
1

No veo las hendiduras profundas como un problema categórico a eliminar (ni veo la refactorización como la verdadera respuesta para todo).

Por lo general, en lugar de ifs anidados, me gusta escribir declaraciones lógicas:

if (foo && bar && baz) 

más bien que

if foo 
 if bar
   if baz
Paul Nathan
fuente
El problema es que también existen bucles for y while que no entran en esta regla.
Tamara Wijsman
@TomWij: No estoy tratando de inducir un imperativo categórico sobre el estilo.
Paul Nathan
1
??? `` `` ``
Tamara Wijsman
1

Yo no lo creía, pero de acuerdo con Code Complete, este es un lugar apropiado para usar break(si su equipo está a bordo). Sin embargo, me imagino que esto es más aceptable con los programadores de C ++, donde se breakusa en switchdeclaraciones que con los programadores de Delphi, donde breaksolo se usa cuando no tiene ganas de escribir un whilebucle.

Peter Turner
fuente
0

La sangría es realmente un pensamiento para luchar, de hecho. Lo que aprendí a hacer es dividir el método en piezas primero, luego usar un truco extraño para omitir cada una de las siguientes piezas si falla una pieza. Aquí hay un ejemplo :

En lugar de :

 {if (networkCardIsOn() == true)
     {if (PingToServer() == true)
        {if (AccesLogin(login,pass) == true)
             {if (nextCondition == true)
                ...
         }
     }
 }

Actualmente escribo:

 {vbContinue = true;

 if (vbContinue) {
       vbContinue = networkCardIsOn();
       if (vbContinue == false) {
             code to Handle This Error();
       } 
 }

 if (vbContinue) {
       vbContinue = PingToServer();
       if (vbContinue == false) {
             code to HandleThisError2();
       } 
 }

 if (vbContinue) {
       vbContinue = AccesLogin(login,pass);
      if (vbContinue == false) {
             HandleThisErrorToo();
       } 
 }
 ...

Al principio, esto me parece extraño, pero desde que lo uso, el costo de mantenimiento se ha dividido a la mitad, y mi cerebro está más fresco al final del día.

De hecho, la ganancia introducida por esta "técnica" es que la complejidad del código está realmente dividida porque el código es menos denso.

Mientras lee el código, no tiene que recordar nada sobre las condiciones pasadas: si está en ese punto X en el código, los pasos anteriores se pasan y han tenido éxito.

Otra ganancia es que se simplifica la "ruta y condición de escape" de todos los "if-else" anidados.

Pierre Watelet
fuente
¿Puede dar más detalles sobre "el costo de mantenimiento se ha dividido a la mitad"? Además, ¿cómo podría saber realmente si se detuviera la ejecución?
Chris
He hecho algunas ediciones. Espero responder a sus preguntas ...
Pierre Watelet
2
Si quiere ir incluso de manera simple, tiene una línea de manejo de error de goto.
Paul Nathan
Eso no es agradable. Lo siento, pero no es así. Por otra parte, soy raro
Murph
3
en lugar de emular un try catch, ¿por qué no usar uno directamente?
Newtopian