LBYL vs EAFP em Java?
-
03-07-2019 - |
Pergunta
Recentemente eu estava me ensinando Python e descobriu os idiomas LBYL / EAFP que diz respeito à verificação de erros antes da execução do código. Em Python, parece que o estilo aceita é EAFP, e parece funcionar bem com o idioma.
LBYL (
def safe_divide_1(x, y):
if y == 0:
print "Divide-by-0 attempt detected"
return None
else:
return x/y
EAFP ( a sua E asier para A sk F orgiveness de P ermission ):
def safe_divide_2(x, y):
try:
return x/y
except ZeroDivisionError:
print "Divide-by-0 attempt detected"
return None
A minha pergunta é esta: eu nunca tinha sequer ouvido falar de usar EAFP como a construção de validação de dados primários, vindo de um Java e C ++ fundo. EAFP é algo que é sábio para usar em Java? Ou há muita sobrecarga de exceções? Eu sei que há apenas sobrecarga quando uma exceção é realmente jogado, por isso estou inseguro quanto às razões pelas quais o método mais simples de EAFP não é usado. É apenas uma preferência?
Solução
Pessoalmente, e acho que isso é apoiada por convenção, EAFP nunca é uma boa maneira de ir. Você pode olhar para ele como um equivalente ao seguinte:
if (o != null)
o.doSomething();
else
// handle
ao contrário:
try {
o.doSomething()
}
catch (NullPointerException npe) {
// handle
}
Além disso, considere o seguinte:
if (a != null)
if (b != null)
if (c != null)
a.getB().getC().doSomething();
else
// handle c null
else
// handle b null
else
// handle a null
Isto pode parecer muito menos elegante (e sim, este é um exemplo grosseiro - bear with me), mas dá-lhe muito maior granularidade em lidar com o erro, ao invés de embalar tudo em um try-catch para obter esse NullPointerException
, e depois tentar descobrir onde e por que você conseguiu.
A forma como eu vejo EAFP nunca deve ser usado, exceto em situações raras. Além disso, desde que você levantou a questão:. sim, o bloco try-catch incorre em alguma sobrecarga , mesmo que a exceção não é lançada
Outras dicas
Se você estiver acessando arquivos, EAFP é mais confiável do que LBYL, porque as operações envolvidas na LBYL não são atômicas, eo sistema de arquivos pode alterar entre o momento em que você olha e o tempo de pular. Na verdade, o nome padrão é TOCTOU - Tempo de Verificação, tempo de uso; erros causados ??pela verificação imprecisas são TOCTOU bugs.
Considere a criação de um arquivo temporário que deve ter um nome único. A melhor maneira de descobrir se o nome do arquivo escolhido ainda existe é tentar criá-la - certificando-se de usar as opções para garantir que sua operação falhar se o arquivo já existe (em termos POSIX / Unix, a bandeira O_EXCL para open()
). Se você tentar testar se o arquivo já existe (provavelmente usando access()
), em seguida, entre o momento em que diz "não" eo tempo que você tentar criar o arquivo, alguém ou alguma coisa pode ter criado o arquivo.
Por outro lado, suponha que você tenta ler um arquivo existente. Seu cheque se o arquivo existe (LBYL) pode dizer "ele está lá", mas quando você realmente abri-lo, você encontra "ele não está lá".
Em ambos os casos, você tem que verificar o funcionamento final - eo LBYL fez não automaticamente ajuda
. (Se você está mexendo com SUID ou SGID programas, access()
faz uma pergunta diferente;. Que podem ser relevantes para LBYL, mas o código ainda tem que levar em conta a possibilidade de falha)
Além do custo relativo de exceções em Python e Java, tenha em mente que há uma diferença na filosofia / atitude entre eles. Java tenta ser muito rigoroso sobre os tipos (e tudo mais), exigindo declarações explícitas e detalhada de assinaturas de classe / método. Ele assume que você deveria saber, em qualquer ponto, exatamente o tipo de objeto que você está usando eo que ele é capaz de fazer. Em contraste, os meios de Python "pato digitação" que você não sabe com certeza (e não deve se importam) que o tipo de manifesto de um objeto é, você só precisa se preocupar que grasna quando você pedir para ele. Neste tipo de ambiente permissivo, a atitude só sane é presumir que as coisas vão funcionar, mas estar pronto para lidar com as consequências se não o fizerem. restritividade natural do Java não se encaixa bem com uma abordagem tão casual. (Isto não se destina a depreciar qualquer abordagem ou linguagem, mas em vez de dizer que essas atitudes são parte da linguagem de cada idioma, e copiar expressões entre diferentes línguas podem muitas vezes levar a constrangimento e falta de comunicação ...)
As exceções são tratadas de forma mais eficiente em Python do que em Java, que é pelo menos parte porque você vê que construção em Python. Em Java, é mais ineficiente (em termos de desempenho) para usar exceções nesse sentido.
Considere estas trechos de código:
def int_or_default(x, default=0):
if x.isdigit():
return int(x)
else:
return default
def int_or_default(x, default=0):
try:
return int(x)
except ValueError:
return default
Ambos olhar correto, certo? Mas um deles não é.
O primeiro, usando LBYL, falhar devido a uma distinção sutil entre isdigit
e isdecimal
; quando chamado com a string "?²³??5", ele irá lançar um erro em vez de retornar corretamente o valor padrão.
O mais tarde, usando EAFTP, resulta em manipulação correta, por definição. Não há espaço para uma incompatibilidade comportamental, porque o código que precisa a exigência é o código que afirma que exigência.
Usando meios LBYL tomar lógica interna e copiá-los em todas chamada local. Ao invés de ter uma codificação canônica de suas necessidades, você receber uma chance livre para mexer-se cada vez que você chamar a função.
É importante notar que EAFTP não sobre exceções e código Java especialmente não deveria estar usando exceções pervasively. É sobre dar o trabalho certo para o bloco de direita do código. Como exemplo, utilizando valores de retorno Optional
é uma maneira perfeitamente válida de escrever código EAFTP, e é muito mais eficaz para garantir a exatidão do que LBYL.