Do livro Code Complete , vem a seguinte citação:
"Coloque o caso normal após o em
if
vez de depois doelse
"
O que significa que exceções / desvios do caminho padrão devem ser colocados no else
caso.
Mas o Programador Pragmático nos ensina a "travar cedo" (p. 120).
Qual regra devo seguir?
if
ramos retornar, use-o primeiro. E evite oelse
restante do código, você já retornou se as pré-condições falharem. Código é mais fácil de ler, menos recuo ...Respostas:
"Crash early" não se refere a qual linha de código vem anteriormente em texto. Ele diz para você detectar erros na etapa mais inicial possível do processamento , para que você não tome decisões e cálculos inadvertidamente com base no estado já defeituoso.
Em uma construção
if
/else
, apenas um dos blocos é executado, portanto, nenhum deles pode ser considerado uma etapa "anterior" ou "posterior". Como encomendá-los é, portanto, uma questão de legibilidade, e "falhar cedo" não entra na decisão.fonte
if/else
construções, provavelmente não importa. Mas aqueles chamados em loop ou com muitas instruções em cada bloco podem executar mais rapidamente com a condição mais comum primeiro.Se a sua
else
declaração contiver apenas código de falha, provavelmente não deverá estar lá.Em vez de fazer isso:
fazem isto
Você não deseja aninhar profundamente seu código simplesmente para incluir a verificação de erros.
E, como todo mundo já afirmou, os dois conselhos não são contraditórios. Um é sobre ordem de execução , o outro é sobre ordem de código .
fonte
if
e um fluxo excepcional no bloco depoiselse
não se aplica se você não tiver umelse
! Declarações de guarda como essa são a forma preferida para lidar com condições de erro na maioria dos estilos de codificação.Você deve seguir os dois.
O conselho "Falha no início" / falha no início significa que você deve testar suas entradas quanto a possíveis erros o mais rápido possível.
Por exemplo, se seu método aceitar um tamanho ou contagem que deve ser positivo (> 0), o aviso antecipado de falha significa que você testa essa condição logo no início do método, em vez de esperar que o algoritmo produza bobagens. resultados.
O conselho para colocar o caso normal primeiro significa que, se você testar uma condição, o caminho mais provável deverá vir primeiro. Isso ajuda no desempenho (como a previsão de ramificação do processador estará correta com mais frequência) e na legibilidade, porque você não precisa pular blocos de código ao tentar descobrir o que a função está fazendo no caso normal.
Esse conselho realmente não se aplica quando você testa uma pré-condição e sai imediatamente (usando declarações ou
if (!precondition) throw
construções), porque não há manipulação de erro a ser ignorada durante a leitura do código.fonte
if(cond){/*more likely code*/}else{/*less likely code*/}
corre mais rápido do queif(!cond){/*less likely code*/}else{/*more likely code*/}
por causa da previsão do ramo. Eu pensaria que a previsão de ramificação não é tendenciosa paraif
aelse
declaração ou a e apenas leva em consideração o histórico. Portanto, seelse
é mais provável que isso aconteça, deve ser capaz de prever isso tão bem. Essa suposição é falsa?Acho que o @JackAidley já disse o essencial , mas deixe-me formulá-lo assim:
sem exceções (por exemplo, C)
No fluxo de código regular, você tem:
No caso de "erro de saída antecipada", seu código diz:
Se você detectar esse padrão - a
return
em umelse
(ou mesmoif
) bloco, refaça-o imediatamente para que o código em questão não tenha umelse
bloco:No mundo real…
Isso evita que o aninhamento seja profundo demais e atenda ao caso “sair cedo” (ajuda a manter a mente - e o fluxo do código - limpos) e não viola o “colocar a coisa mais provável na
if
parte”, porque simplesmente não háelse
parte .C
e limpezaInspirado por uma resposta em uma pergunta semelhante (que entendeu errado), veja como você faz a limpeza com C. Você pode usar um ou dois pontos de saída, aqui está um para dois pontos de saída:
Você pode recolhê-los em um ponto de saída se houver menos limpeza a fazer:
Esse uso de
goto
é perfeitamente bom, se você puder lidar com isso; o conselho para não usargoto
é dirigido a pessoas que ainda não podem decidir por si mesmas se um uso é bom, aceitável, ruim, código de espaguete ou qualquer outra coisa.Exceções
O texto acima fala sobre idiomas sem exceções, o que eu prefiro muito (posso usar o tratamento explícito de erros muito melhor e com muito menos surpresa). Para citar igli:
Mas aqui está uma sugestão de como você o faz bem em um idioma com exceções e quando você deseja usá-los bem:
retorno de erro em face de exceções
Você pode substituir a maioria dos primeiros
return
s com lançando uma exceção. No entanto , o fluxo normal do programa, ou seja, qualquer fluxo de código no qual o programa não tenha encontrado, bem, uma exceção ... uma condição de erro ou algo assim, não deve gerar exceções.Isso significa que…
... está bem, mas ...
… não é. Basicamente, uma exceção não é um elemento de fluxo de controle . Isso também faz com que o Operations pareça estranho para você (“esses programadores Java ™ sempre nos dizem que essas exceções são normais”) e pode dificultar a depuração (por exemplo, diga ao IDE para interromper qualquer exceção). As exceções geralmente exigem que o ambiente de tempo de execução desenrole a pilha para produzir rastreios, etc. Há provavelmente mais razões contra isso.
Isso se resume a: em um idioma que suporta exceções, use o que corresponder à lógica e estilo existentes e parecer natural. Se escrever algo do zero, faça isso com antecedência. Se estiver escrevendo uma biblioteca do zero, pense nos seus consumidores. (Também nunca use
abort()
em uma biblioteca ...) Mas, faça o que fizer, como regra geral, não haverá uma exceção se a operação continuar (mais ou menos) normalmente após ela.conselho geral wrt. Exceções
Tente obter todo o uso de exceções no programa acordado por toda a equipe de desenvolvedores primeiro. Basicamente, planeje-os. Não os use em abundância. Às vezes, mesmo em C ++, Java ™, Python, um retorno de erro é melhor. Às vezes não é; use-os com o pensamento.
fonte
goto fail;
oculto na identificação.Na minha opinião, 'Guard Condition' é uma das melhores e mais fáceis maneiras de tornar o código legível. Eu realmente odeio quando vejo
if
no início do método e não vejo oelse
código porque está fora da tela. Eu tenho que rolar para baixo apenas para verthrow new Exception
.Coloque as verificações no início, para que a pessoa que está lendo o código não precise pular todo o método para lê-lo, mas sempre faça uma varredura de cima para baixo.
fonte
(A resposta dos @mirabilos é excelente, mas eis como eu penso sobre a questão para chegar à mesma conclusão :)
Estou pensando em mim (ou em outra pessoa) lendo o código da minha função mais tarde. Quando leio a primeira linha, não posso fazer suposições sobre minha entrada (exceto aquelas que não verificarei de qualquer maneira). Então, meu pensamento é "Ok, eu sei que vou fazer as coisas com meus argumentos. Mas primeiro vamos limpá-los" - ou seja, matar os caminhos de controle nos quais eles não são do meu agrado. "Mas, ao mesmo tempo , Não vejo o caso normal como algo condicionado, quero enfatizar que é normal.
fonte
Esse tipo de ordenação de condições depende da criticidade da seção de código em questão e da existência de padrões que podem ser usados.
Em outras palavras:
A. seção crítica e sem padrões => Fail Early
B. seção e padrões não críticos => Usar padrões na outra parte
C. casos intermediários => decidir por caso, conforme necessário
fonte