Como evitar recuos profundos? [fechadas]


17

Quais etapas e medidas posso tomar para evitar recuos profundos no meu código?


2
Muitas pessoas vão falar sobre refatoração aqui. Talvez isso seja pedir demais, mas se você postou algum código (não muito longo) que é profundamente recuado e as pessoas podem mostrar como elas o refatoram. Claro, que provavelmente faz com que a linguagem específica pergunta então ...
Paddyslacker

3
Use uma largura menor da guia.
Mipadi

6
Anti-padrão de ponta de seta .
Pesquise

2
Pare de usar python: D
back2dos

É hora de analisar seu controle e sua lógica de loop. Provavelmente, seu código é mais complicado do que precisa e uma re-conceituação do problema levará a um código muito mais curto. Estude um bom código e aprenda as técnicas.
Macneil

Respostas:


14

O recuo profundo geralmente não é um problema se todas as funções / métodos do seu programa fizerem uma e apenas uma coisa. Ocasionalmente, pode ser necessário aninhar condicionais com alguns níveis de profundidade, mas posso dizer honestamente que só escrevi código recuado profundamente algumas vezes em mais de 12 anos de codificação.


26

A melhor coisa que você pode fazer é extrair 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);
    }

    // ....
}

2
Isso também funciona para condições complexas if. Levado ao extremo, você terminará com pseudocódigo executável.
Alan Plum

Outra melhor coisa que podemos fazer é provavelmente eliminar elseblocos desnecessários .
sepehr 22/03/18

16

Talvez você possa considerar cláusulas de guarda ?

ao invés de

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

Faz

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
}

Se você tiver uma chance, eu recomendo que você leia Code Complete, de Steve McConnell. Ele tem muitos conselhos excelentes sobre esses tópicos.

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

Para obter mais informações sobre "cláusulas de guarda", consulte: https://sourcemaking.com/refactoring/replace-nested-conditional-with-guard-clauses


8

Inverta seus ifs.

Ao invés de:

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

Eu escreveria:

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

O mesmo se aplica a if- elseblocos. Se elsefor mais curto / menos aninhado, revertê-los.

Verifique os valores dos parâmetros em um só lugar

Verifique todos os parâmetros em busca de valores ilegais assim que você inserir seu método e continue sabendo que está seguro. Isso cria um código mais legível, mas também evita que você empilhe blocos condicionais posteriormente e espalhe essas verificações por toda a sub-rotina.


1
Esse estilo tem um nome específico?
Thomas Lauria

@ThomasLauria não que eu saiba. Está saindo cedo. Ifs no início do código que interrompe o fluxo de execução devido a alguma condição não ser atendida também são conhecidos como cláusulas de salvaguarda , como apontou @JasonTuran. E isso parece ser o mais próximo possível de ter um nome distinto.
Konrad Morawski

alguns anos atrás, meu supervisor me disse que esse estilo foi nomeado como "programação linear", mas acho que isso era um fantasma dele;)
Thomas Lauria

4

Normalmente, vi que o código profundamente recuado é geralmente um código problemático. Se você estiver enfrentando esse problema, dê um passo atrás e avalie se sua função está fazendo muitas coisas.

Ao mesmo tempo, para responder sua pergunta, se houver uma necessidade de indentação tão profunda, sugiro que você a deixe lá. Pela simples razão de que, em tal código, o recuo ajudará, pois é provável que seja um trecho muito longo.


2

Divida os componentes aninhados (especialmente os repetidos) em funções separadas (isso é mais fácil se o seu idioma suportar fechamentos) ou substitua uma série de loops aninhados por uma recursão.

Recue também dois espaços em vez de quatro.


5
Uma vez que você tenha mudado a largura da guia, estará em apuros ...
Daenyth

Tabs Two-espaciais são o material duro ....
David Thornley

6
Alterar o travessão é apenas uma maneira de esconder o problema não é uma solução
Murph

1

Não vejo indentações profundas como um problema categórico a ser removido (nem vejo a refatoração como a verdadeira resposta para tudo).

Normalmente, em vez de ifs aninhados, gosto de escrever instruções lógicas:

if (foo && bar && baz) 

ao invés de

if foo 
 if bar
   if baz

O problema é que também existem e enquanto loops que não se enquadram nessa regra.
Tamara Wijsman

@ TomWij: Não estou tentando induzir um imperativo categórico sobre o estilo.
Paul Nathan

1
??? `` `` ``
Tamara Wijsman

1

Eu mesmo não acreditei, mas, de acordo com o Code Complete, este é um local apropriado para usar break(se sua equipe estiver a bordo). Eu imagino que isso seja mais aceitável com programadores C ++, onde é breakusado em switchdeclarações do que com programadores Delphi, onde breaké usado somente quando você não deseja escrever um whileloop.


0

Recuo é realmente um pensamento para lutar, de fato. O que eu aprendi a fazer é dividir o método em partes primeiro e depois usar um truque estranho para pular todas as partes seguintes, se uma delas falhar. Aqui está um exemplo :

Ao invés de :

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

Atualmente, escrevo:

 {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();
       } 
 }
 ...

Isso me pareceu estranho no começo, mas desde que eu uso isso, o custo de manutenção foi dividido pela metade e meu cérebro fica mais frio no final do dia.

De fato, o ganho introduzido por essa "técnica" é que a complexidade do código é realmente dividida porque o código é menos denso.

Ao ler o código, você não precisa se lembrar de nada sobre as condições passadas: se você estiver no ponto X do código, as etapas anteriores são aprovadas e foram bem-sucedidas.

Outro ganho é que o "caminho e condição de escape" de todos os aninhados "if-else" é simplificado.


Você pode elaborar sobre "o custo de manutenção foi dividido pela metade" Além disso, como você realmente saberia qual se interrompesse a execução?
Chris

Eu fiz algumas edições. Espero responder às suas perguntas ...
Pierre Watelet

2
Se você quiser ir simplesmente, você tem uma linha de giro error_handling.
Paul Nathan

Isso não é legal. Desculpe, mas não é. Então, novamente eu sou estranho
Murph

3
em vez de emular uma tentativa, por que não usar uma diretamente?
Newtopian
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.