Eu tenho uma situação em que estou tentando recuperar um objeto. Se a pesquisa falhar, tenho vários substitutos, cada um dos quais pode falhar. Portanto, o código se parece com:
try {
return repository.getElement(x);
} catch (NotFoundException e) {
try {
return repository.getSimilarElement(x);
} catch (NotFoundException e1) {
try {
return repository.getParentElement(x);
} catch (NotFoundException e2) {
//can't recover
throw new IllegalArgumentException(e);
}
}
}
Isso parece muito feio. Eu odeio retornar nulo, mas é melhor nessa situação?
Element e = return repository.getElement(x);
if (e == null) {
e = repository.getSimilarElement(x);
}
if (e == null) {
e = repository.getParentElement(x);
}
if (e == null) {
throw new IllegalArgumentException();
}
return e;
Existem outras alternativas?
O uso de blocos try-catch aninhados é um anti-padrão? está relacionado, mas as respostas existem no sentido de "às vezes, mas geralmente é evitável", sem dizer quando ou como evitá-lo.
java
exception-handling
Alex Wittig
fonte
fonte
NotFoundException
algo realmente excepcional?Respostas:
A maneira usual de eliminar o aninhamento é usar funções:
Se essas regras de fallback forem universais, considere implementar isso diretamente no
repository
objeto, onde você poderá usar apenasif
instruções simples em vez de uma exceção.fonte
method
seria uma palavra melhor quefunction
.Isso seria realmente fácil com algo como uma mônada da Option. Infelizmente, o Java não os possui. Em Scala, eu usaria o
Try
tipo para encontrar a primeira solução bem-sucedida.Na minha mentalidade de programação funcional, eu configurava uma lista de retornos de chamada representando as várias fontes possíveis e as percorria até encontrar a primeira bem-sucedida:
Isso pode ser recomendado apenas se você realmente tiver um grande número de fontes ou se precisar configurá-las em tempo de execução. Caso contrário, essa é uma abstração desnecessária e você lucraria mais se mantivesse seu código simples e estúpido, e apenas usasse essas feias tentativas aninhadas.
fonte
Try
tipo em Scala, por mencionar mônadas e pela solução usando um loop.Optional
mônada ( prova ) já estava liberado.Se você está antecipando que muitas dessas chamadas de repositório serão lançadas
NotFoundException
, use um wrapper em torno do repositório para otimizar o código. Eu não recomendaria isso para operações normais, lembre-se:fonte
Por sugestão da @on, aqui está uma resposta mais monádica. É uma versão bastante simplificada, na qual você deve aceitar algumas suposições:
a função "unit" ou "return" é o construtor da classe
a operação "bind" acontece no momento da compilação, portanto é oculta da invocação
as funções "action" também são vinculadas à classe em tempo de compilação
embora a classe seja genérica e envolva qualquer classe arbitrária E, acho que é realmente um exagero nesse caso. Mas deixei assim como um exemplo do que você poderia fazer.
Com essas considerações, a mônada se traduz em uma classe de wrapper fluente (embora você esteja perdendo muita flexibilidade que obteria em uma linguagem puramente funcional):
(isso não será compilado ... alguns detalhes são deixados inacabados para manter a amostra pequena)
E a invocação ficaria assim:
Observe que você tem a flexibilidade de compor as operações de "busca" como desejar. Parará quando receber uma resposta ou uma exceção diferente da não encontrada.
Eu fiz isso muito rápido; não está certo, mas espero que transmita a ideia
fonte
repository.fetchElement().fetchParentElement().fetchSimilarElement();
- na minha opinião: Código de mal (no sentido dado por Jon Skeet)return this
para criar chamadas de objetos em cadeia existe há muito tempo. Como o OO envolve objetos mutáveis,return this
é mais ou menos equivalente areturn null
sem encadeamento. No entanto,return new Thing<E>
abre a porta para outro recurso que este exemplo não aborda, por isso é importante para esse padrão se você optar por seguir esse caminho.CustomerBuilder.withName("Steve").withID(403)
e esse código, porque, ao vermos.fetchElement().fetchParentElement().fetchSimilarElement()
, não está claro o que acontece, e essa é a chave aqui. Todos eles são buscados? Não é acumulativo neste caso e, portanto, não é tão intuitivo. Eu preciso ver issoif (answer != null) return this
antes de realmente entender. Talvez seja apenas uma questão de nomeação adequada (orFetchParent
), mas é "mágica" de qualquer maneira.answer
ingetAnswer
e redefinir (limpar) oanswer
próprio campo antes de retornar seu valor. Caso contrário, isso meio que quebra o princípio da separação de comando / consulta, porque pedir para buscar um elemento (consulta) altera o estado do seu objeto de repositório (answer
nunca é redefinido) e afeta o comportamento defetchElement
quando você o chamar da próxima vez. Sim, estou falando um pouco, acho que a resposta é válida, não fui eu quem votou contra.Outra maneira de estruturar uma série de condições como essa é carregar uma bandeira ou testar se é nulo (melhor ainda, use o Guava's Optional para determinar quando uma boa resposta está presente) para encadear as condições.
Dessa forma, você está observando o estado do elemento e fazendo as chamadas corretas com base em seu estado - ou seja, desde que você ainda não tenha uma resposta.
(Eu concordo com o @amon, no entanto. Eu recomendo olhar para um padrão Mônada, com um objeto wrapper como
class Repository<E>
esse com membrosE answer;
eException error;
. Em cada estágio, verifique se há uma exceção e, se houver, pule cada etapa restante. no final, você fica com uma resposta, a ausência de uma resposta ou uma exceção e pode decidir o que fazer com isso.)fonte
Primeiro, parece-me que deve haver uma função como
repository.getMostSimilar(x)
(você deve escolher um nome mais apropriado), pois parece haver uma lógica usada para encontrar o elemento mais próximo ou mais semelhante de um determinado elemento.O repositório pode então implementar a lógica, como mostrado no post de amons. Isso significa que o único caso em que uma exceção deve ser lançada é quando não há um único elemento que possa ser encontrado.
No entanto, isso só é possível se as lógicas para encontrar o elemento mais próximo puderem ser encapsuladas no repositório. Se isso não for possível, forneça mais informações sobre como (por quais critérios) o elemento mais próximo pode ser escolhido.
fonte