Devo retornar de uma função cedo ou usar uma instrução if? [fechadas]

283

Eu sempre escrevi esse tipo de função em ambos os formatos, e fiquei me perguntando se um formato é preferido em detrimento de outro e por quê.

public void SomeFunction(bool someCondition)
{
    if (someCondition)
    {
        // Do Something
    }
}

ou

public void SomeFunction(bool someCondition)
{
    if (!someCondition)
        return;

    // Do Something
}

Eu geralmente codifico com o primeiro, pois é assim que meu cérebro funciona enquanto codifico, embora eu ache que prefiro o segundo, já que ele cuida de qualquer tratamento de erros imediatamente e eu acho mais fácil ler

    
por Rachel 07.07.2017 / 18:43
fonte

19 respostas

379

Eu prefiro o segundo estilo. Obtenha casos inválidos fora do caminho primeiro, seja simplesmente saindo ou criando exceções conforme apropriado, coloque uma linha em branco e adicione o corpo "real" do método. Eu acho mais fácil de ler.

    
por 11.11.2010 / 20:27
fonte
157

Definitivamente, o último. O primeiro não parece ruim agora, mas quando você obtém um código mais complexo, não consigo imaginar que alguém pense:

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

é mais legível que

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}

Admito que nunca compreendi a vantagem de pontos de saída únicos.

    
por 07.07.2017 / 18:44
fonte
31

Depende - Em geral, não vou sair do meu caminho para tentar mover um monte de código para sair da função cedo - o compilador geralmente cuidará disso para mim. Dito isto, se houver alguns parâmetros básicos no topo que eu preciso e não posso continuar de outra forma, vou sair cedo. Da mesma forma, se uma condição gerar um bloco gigante if na função, eu a verificarei cedo como resultado disso também.

No entanto, se uma função requer alguns dados quando é chamada, normalmente estou lançando uma exceção (veja o exemplo) em vez de apenas retornar.

public int myFunction(string parameterOne, string parameterTwo) {
  // Can't work without a value
  if (string.IsNullOrEmpty(parameterOne)) {
    throw new ArgumentNullException("parameterOne");
  } 
  if (string.IsNullOrEmpty(parameterTwo)) {
    throw new ArgumentNullException("parameterTwo");
  }

  // ...      
  // Do some work
  // ...

  return value;
}
    
por 07.07.2017 / 18:44
fonte
23

Eu prefiro o retorno antecipado.

Se você tem um ponto de entrada e um ponto de saída, você sempre tem que rastrear todo o código em sua cabeça até o ponto de saída (você nunca sabe se algum outro código abaixo faz outra coisa para o resultado , então você tem que rastreá-lo até que exista). Você faz isso não importa qual ramo determina o resultado final. Isso é difícil de seguir.

Com uma entrada e múltiplas, você retorna quando você tem seu resultado e não se incomoda em rastreá-lo até o fim para ver que ninguém faz mais nada (porque não haverá mais nada desde que você retornou) . É como ter o corpo do método dividido em mais etapas, cada passo com a possibilidade de retornar o resultado ou deixar o próximo passo tentar sua sorte.

    
por 11.11.2010 / 21:50
fonte
12

Na programação C, onde você tem que limpar manualmente, há muito a ser dito para um retorno de um ponto. Mesmo se não houver necessidade agora de limpar algo, alguém pode editar sua função, alocar algo e precisar limpá-la antes de retornar. Se isso acontecer, será um trabalho de pesadelo, examinando todas as declarações de retorno.

Na programação em C ++ você tem destruidores e até mesmo protetores de saída de espaço. Tudo isso precisa estar aqui para garantir que o código seja seguro contra exceções, portanto, o código está bem protegido contra a saída antecipada e, portanto, não tem nenhuma desvantagem lógica e é puramente um problema de estilo.

Eu não tenho conhecimento suficiente sobre Java, se o código de bloco "finalmente" será chamado e se os finalizadores podem lidar com a situação de necessidade de garantir que algo aconteça.

C # Eu certamente não posso responder.

A linguagem D fornece proteções de saída de escopos embutidas e, portanto, está bem preparada para a saída antecipada e, portanto, não deve apresentar um problema além do estilo.

As funções não devem, é claro, demorar tanto tempo, e se você tiver uma instrução switch importante, seu código provavelmente também será mal fatorado.

    
por 18.02.2011 / 14:21
fonte
8

Retorno antecipado para a vitória. Eles podem parecer feios, mas muito menos feios do que grandes wrappers if , especialmente se houver várias condições para verificar.

    
por 11.11.2010 / 20:26
fonte
8

eu uso os dois.

Se DoSomething for de 3 a 5 linhas de código, o código ficará bonito usando o primeiro método de formatação.

Mas se tiver mais linhas do que isso, prefiro o segundo formato. Não gosto quando os colchetes de abertura e fechamento não estão na mesma tela.

    
por 12.11.2010 / 19:10
fonte
7

Uma razão clássica para a saída única de única entrada é que, de outra forma, a semântica formal torna-se indescritivelmente feia, caso contrário (mesmo motivo, GOTO foi considerado prejudicial).

Em outras palavras, é mais fácil pensar sobre quando o seu software sairá da rotina se você tiver apenas 1 retorno. O que também é um argumento contra exceções.

Geralmente, minimizo a abordagem de retorno antecipado.

    
por 11.11.2010 / 20:40
fonte
6

Pessoalmente, prefiro fazer verificações de condição de aprovação / reprovação no início. Isso me permite agrupar a maioria das falhas mais comuns no topo da função com o restante da lógica a seguir.

    
por 11.11.2010 / 20:26
fonte
5

Depende.

Retorno antecipado se houver alguma condição óbvia de ponto final a ser verificada imediatamente, o que tornaria o resto da função sem sentido. *

Defina Retval + retorno único se a função for mais complexa e puder ter vários pontos de saída de outra forma (problema de legibilidade).

* Isso pode indicar um problema de design. Se você descobrir que muitos dos seus métodos precisam verificar algum estado externo / de paramater ou algo parecido antes de executar o restante do código, provavelmente é algo que deve ser tratado pelo chamador.

    
por 11.11.2010 / 21:22
fonte
2

Use um If

No livro de Don Knuth sobre o GOTO, eu o li dar uma razão para sempre ter a condição mais provável em primeiro lugar em uma declaração if. Sob o pressuposto de que isso ainda é uma idéia razoável (e não uma de pura consideração pela velocidade da época). Eu diria que os retornos iniciais não são uma boa prática de programação, especialmente considerando o fato de que eles são mais frequentemente usados para tratamento de erros, a menos que seu código tenha maior probabilidade de falhar do que não :-)

Se você seguir o conselho acima, você precisaria colocar esse retorno na parte inferior da função, e então você pode nem mesmo chamá-lo de um retorno lá, apenas defina o código de erro e retorne duas linhas daqui . Obtendo assim a 1 entrada 1 saída ideal.

Específicos do Delphi ...

Estou convencido de que esta é uma boa prática de programação para programadores Delphi, embora eu não tenha nenhuma prova. Pré-D2009, não temos uma maneira atômica de retornar um valor, temos exit; e result := foo; ou podemos simplesmente lançar exceções.

Se você tivesse que substituir

if (true) {
 return foo;
} 

para

if true then 
begin
  result := foo; 
  exit; 
end;

você pode ficar cansado de ver isso no topo de todas as suas funções e preferir

if false then 
begin
  result := bar;

   ... 
end
else
   result := foo;

e evite exit .

    
por 07.07.2017 / 18:46
fonte
1

Concordo com a seguinte declaração:

I'm personally a fan of guard clauses (the second example) as it reduces the indenting of the function. Some people don't like them because it results in multiple return points from the function, but I think it's clearer with them.

Extraído de esta pergunta em stackoverflow .

    
por 23.05.2017 / 14:40
fonte
1

Eu uso retornos antecipados quase exclusivamente nos dias de hoje, ao extremo. Eu escrevo isso

self = [super init];

if (self != nil)
{
    // your code here
}

return self;

como

self = [super init];
if (!self)
    return;

// your code here

return self;

mas isso realmente não importa. Se você tiver mais de um ou dois níveis de aninhamento em suas funções, eles precisam ser bloqueados.

    
por 07.07.2017 / 18:48
fonte
1

Eu preferiria escrever:

if(someCondition)
{
    SomeFunction();
}
    
por 07.07.2017 / 18:49
fonte
1

Como você, eu geralmente escrevo o primeiro, mas prefiro o último. Se eu tiver muitas verificações aninhadas, normalmente refatorarei o segundo método.

Não gosto de como o tratamento de erros é removido da verificação.

if not error A
  if not error B
    if not error C
      // do something
    else handle error C
  else handle error B
else handle error A

Eu prefiro isso:

if error A
  handle error A; return
if error B
  handle error B; return
if error C
  handle error C; return

// do something
    
por 07.07.2017 / 18:50
fonte
0

As condições no topo são chamadas de "condições prévias". Ao colocar if(!precond) return; , você está listando visualmente todas as condições prévias.

Usar o grande bloco "if-else" pode aumentar a sobrecarga de recuo (esqueci a citação sobre recuos de 3 níveis).

    
por 03.08.2016 / 04:41
fonte
-1

Eu prefiro manter se as declarações forem pequenas.

Então, escolhendo entre:

if condition:
   line1
   line2
    ...
   line-n

e

if not condition: return

line1
line2
 ...
line-n

Eu escolheria o que você descreveu como "retorno antecipado".

Lembre-se, eu não me preocupo com retorno antecipado ou o que for, eu apenas gosto de simplificar o código, encurtar o corpo de declarações if, etc.

Aninhados se e por enquanto e enquanto são horríveis , evite-os a todo custo.

    
por 07.07.2017 / 18:50
fonte
-2

Como outros dizem, isso depende. Para pequenas funções que retornam valores, posso codificar retornos antecipados. Mas, para funções de tamanho considerável, gosto de ter sempre um lugar no código onde sei que posso colocar algo que será executado antes de retornar.

    
por 11.11.2010 / 20:37
fonte
-2

Eu pratico fail-fast no nível da função. Ele mantém o código consistente e limpo (para mim e para aqueles com quem já trabalhei). Por isso sempre volto cedo.

Para algumas condições verificadas frequentemente, você pode implementar aspectos para essas verificações se usar AOP.

    
por 11.11.2010 / 20:38
fonte