Estou preso em decidir como lidar com exceções em meu aplicativo.
Muito se meus problemas com exceções vierem de 1) acessar dados por meio de um serviço remoto ou 2) desserializar um objeto JSON. Infelizmente, não posso garantir o sucesso para nenhuma dessas tarefas (corte de conexão de rede, objeto JSON malformado que está fora do meu controle).
Como resultado, se eu encontrar uma exceção, simplesmente a capturo dentro da função e retorno FALSE para o chamador. Minha lógica é que tudo o que o chamador realmente se preocupa é se a tarefa foi bem-sucedida, e não por que não foi bem-sucedida.
Aqui está um exemplo de código (em JAVA) de um método típico)
public boolean doSomething(Object p_somthingToDoOn)
{
boolean result = false;
try{
// if dirty object then clean
doactualStuffOnObject(p_jsonObject);
//assume success (no exception thrown)
result = true;
}
catch(Exception Ex)
{
//don't care about exceptions
Ex.printStackTrace();
}
return result;
}
Acho que essa abordagem é boa, mas estou muito curioso para saber quais são as melhores práticas para gerenciar exceções (devo realmente fazer uma exceção borbulhar em toda a pilha de chamadas?).
Em resumo das principais questões:
- É correto apenas capturar as exceções, mas não colocá-las em bolhas ou notificar formalmente o sistema (por meio de um log ou uma notificação ao usuário)?
- Quais são as práticas recomendadas para exceções que não resultam em tudo que exige um bloco try / catch?
Acompanhamento / Editar
Obrigado por todos os comentários, encontramos algumas fontes excelentes sobre gerenciamento de exceções online:
- Melhores práticas para tratamento de exceções | O'Reilly Media
- Práticas recomendadas para lidar com exceções em .NET
- Melhores práticas: gerenciamento de exceções (o artigo agora aponta para a cópia archive.org)
- Antipadrões de tratamento de exceções
Parece que o gerenciamento de exceções é uma daquelas coisas que variam com base no contexto. Mas o mais importante, deve-se ser consistente em como eles gerenciam exceções dentro de um sistema.
Além disso, tome cuidado com a podridão de código por meio de try / catches excessivos ou não dando respeito a uma exceção (uma exceção é alertar o sistema, o que mais precisa ser avisado?).
Além disso, este é um comentário bem escolhido de m3rLinEz .
Tendo a concordar com Anders Hejlsberg e você, a maioria das pessoas que ligam só se importam se a operação foi bem-sucedida ou não.
A partir deste comentário, surgem algumas questões para pensar ao lidar com exceções:
- Qual é o ponto dessa exceção sendo lançada?
- Como faz sentido lidar com isso?
- O chamador realmente se preocupa com a exceção ou apenas se importa se a chamada foi bem-sucedida?
- Forçar um chamador a gerenciar uma exceção potencial é normal?
- Você está sendo respeitoso com as expressões da língua?
- Você realmente precisa retornar um sinalizador de sucesso como booleano? Retornar booleano (ou um int) é mais uma mentalidade C do que Java (em Java, você apenas trataria da exceção).
- Siga as construções de gerenciamento de erros associadas ao idioma :)!
fonte
Respostas:
Parece-me estranho que você queira capturar exceções e transformá-las em códigos de erro. Por que você acha que o chamador prefere códigos de erro em vez de exceções, quando o último é o padrão em Java e C #?
Quanto às suas perguntas:
fonte
Isso depende da aplicação e da situação. Se você está construindo um componente de biblioteca, você deve borbulhar as exceções, embora elas devam ser agrupadas para serem contextuais com seu componente. Por exemplo, se você está criando um banco de dados Xml e digamos que está usando o sistema de arquivos para armazenar seus dados, e está usando as permissões do sistema de arquivos para proteger os dados. Você não gostaria de criar uma exceção FileIOAccessDenied, pois isso vaza sua implementação. Em vez disso, você envolveria a exceção e lançaria um erro AccessDenied. Isso é especialmente verdadeiro se você distribuir o componente a terceiros.
Quanto a se não há problema em engolir exceções. Isso depende do seu sistema. Se o seu aplicativo pode lidar com os casos de falha e não há benefício em notificar o usuário sobre o motivo da falha, vá em frente, embora seja altamente recomendável que você registre a falha. Sempre achei frustrante ser chamado para ajudar a solucionar um problema e descobrir que eles estavam engolindo a exceção (ou substituindo-a e lançando uma nova, sem definir a exceção interna).
Em geral, eu uso as seguintes regras:
Acho que o seguinte código é um cheiro:
Um código como este não serve para nada e não deve ser incluído.
fonte
// do something
incluir qualquertry/finally
bloco ao redor do ponto que lança, osfinally
blocos serão executados antes docatch
bloco. Sem otry/catch
, a exceção voará até o topo da pilha sem que nenhumfinally
bloco seja executado. Isso permite que o manipulador de nível superior decida se deve ou não executar osfinally
blocos.Eu gostaria de recomendar outra boa fonte sobre o assunto. É uma entrevista com os inventores do C # e Java, Anders Hejlsberg e James Gosling, respectivamente, sobre o tópico Exceção Verificada do Java.
Falha e exceções
Existem também ótimos recursos na parte inferior da página.
Tendo a concordar com Anders Hejlsberg e você, a maioria das pessoas que ligam só se importam se a operação foi bem-sucedida ou não.
EDIT: Adicionados mais detalhes sobre o converstaion
fonte
FetchData
e lançar uma exceção de um tipo inesperado, ele não terá como saber se essa exceção significa simplesmente que os dados estão indisponíveis (nesse caso, a capacidade do código de sobreviver sem ele os "resolveria") ou se isso significa que a CPU está em chamas e o sistema deve fazer um "desligamento de segurança" na primeira oportunidade. Parece que Hejlsberg está sugerindo que o código deve assumir o primeiro; talvez seja a melhor estratégia possível, dadas as hierarquias de exceção existentes, mas parece nojenta mesmo assim.As exceções verificadas são um assunto controverso em geral, e em Java em particular (mais tarde tentarei encontrar alguns exemplos para aqueles que são a favor e contra).
Como regra geral, o tratamento de exceções deve ser algo em torno dessas diretrizes, sem uma ordem específica:
printStackTrace()
ou algo parecido, as chances são de que um de seus usuários obterá um desses rastreamentos de pilha eventualmente e não terá nenhum conhecimento sobre o que fazer com ele.Exception
, pois é muito provável que engula exceções importantes.Error
s !! , significando: Never catchThrowable
s comoError
s são subclasses do último.Error
s são problemas que você provavelmente nunca será capaz de lidar (por exemploOutOfMemory
, ou outros problemas JVM)Em relação ao seu caso específico, certifique-se de que qualquer cliente que chame seu método receberá o valor de retorno adequado. Se algo falhar, um método de retorno booleano pode retornar falso, mas certifique-se de que os locais em que você chama esse método são capazes de lidar com isso.
fonte
Você só deve capturar as exceções com as quais pode lidar. Por exemplo, se você estiver lidando com a leitura em uma rede e o tempo de conexão expirar e você obtiver uma exceção, pode tentar novamente. No entanto, se você está lendo em uma rede e obtém uma exceção IndexOutOfBounds, você realmente não pode lidar com isso porque você não (bem, neste caso você não vai) saber o que causou isso. Se você vai retornar falso ou -1 ou nulo, certifique-se de que seja para exceções específicas. Não quero que uma biblioteca que estou usando retorne um falso em uma leitura de rede quando a exceção lançada é que o heap está sem memória.
fonte
As exceções são erros que não fazem parte da execução normal do programa. Dependendo do que o programa faz e de seus usos (ou seja, um processador de texto versus um monitor cardíaco), você desejará fazer coisas diferentes quando encontrar uma exceção. Trabalhei com código que usa exceções como parte da execução normal e é definitivamente um cheiro de código.
Ex.
Este código me faz vomitar. IMO, você não deve se recuperar de exceções, a menos que seja um programa crítico. Se você lançar exceções, coisas ruins estão acontecendo.
fonte
Todos os itens acima parecem razoáveis e, muitas vezes, seu local de trabalho pode ter uma política. Em nosso local definimos os tipos de Exceção:
SystemException
(não marcada) eApplicationException
(marcada).Concordamos que
SystemException
é improvável que s sejam recuperáveis e serão tratados uma vez no topo. Para proporcionar ainda mais contexto, os nossosSystemException
s são exteneded para indicar onde ocorreu, por exemploRepositoryException
,ServiceEception
, etc.ApplicationException
s podem ter um significado comercial comoInsufficientFundsException
e devem ser tratados pelo código do cliente.Sem um exemplo concreto, é difícil comentar sua implementação, mas eu nunca usaria códigos de retorno, eles são um problema de manutenção. Você pode engolir uma Exceção, mas precisa decidir por que e sempre registrar o evento e o rastreamento de pilha. Por último, como seu método não tem outro processamento, ele é bastante redundante (exceto para encapsulamento?), Portanto,
doactualStuffOnObject(p_jsonObject);
poderia retornar um booleano!fonte
Depois de pensar um pouco e examinar seu código, parece-me que você está simplesmente relançando a exceção como um booleano. Você poderia apenas deixar o método passar por essa exceção (você nem mesmo precisa capturá-la) e lidar com ela no chamador, já que é aí que isso importa. Se a exceção fará com que o chamador tente novamente esta função, o chamador deve ser o único capturando a exceção.
Às vezes, pode acontecer que a exceção que você está encontrando não faça sentido para o chamador (ou seja, é uma exceção de rede); nesse caso, você deve envolvê-la em uma exceção específica do domínio.
Se, por outro lado, a exceção sinaliza um erro irrecuperável em seu programa (ou seja, o resultado final dessa exceção será o encerramento do programa), eu pessoalmente gosto de tornar isso explícito capturando-o e lançando uma exceção de tempo de execução.
fonte
Se você for usar o padrão de código em seu exemplo, chame-o de TryDoSomething e capture apenas exceções específicas.
Considere também o uso de um Filtro de Exceção ao registrar exceções para fins de diagnóstico. VB tem suporte de idioma para filtros de exceção. O link para o blog de Greggm tem uma implementação que pode ser usada em C #. Filtros de exceção têm melhores propriedades para depuração em relação a captura e relançamento. Especificamente, você pode registrar o problema no filtro e permitir que a exceção continue a se propagar. Esse método permite anexar um depurador JIT (Just in Time) para ter a pilha original completa. Um relançamento corta a pilha no ponto em que foi relançada.
Os casos em que TryXXXX faz sentido são quando você está envolvendo uma função de terceiros que lança casos que não são verdadeiramente excepcionais ou são simples e difíceis de testar sem chamar a função. Um exemplo seria algo como:
Se você usa um padrão como TryXXX ou não, é mais uma questão de estilo. A questão de capturar todas as exceções e engoli-las não é uma questão de estilo. Certifique-se de que exceções inesperadas possam se propagar!
fonte
Eu sugiro seguir suas dicas da biblioteca padrão para a linguagem que você está usando. Não posso falar em C #, mas vamos dar uma olhada em Java.
Por exemplo, java.lang.reflect.Array tem um
set
método estático :A maneira C seria
... com o valor de retorno sendo um indicador de sucesso. Mas você não está mais no mundo C.
Depois de abraçar as exceções, você deve descobrir que isso torna seu código mais simples e claro, movendo o código de tratamento de erros para longe de sua lógica central. Procure ter muitas instruções em um único
try
bloco.Como outros observaram - você deve ser o mais específico possível no tipo de exceção que detecta.
fonte
Se você capturar uma exceção e retornar falso, deve ser uma exceção muito específica. Você não está fazendo isso, você está pegando todos eles e retornando falsos. Se eu obtiver uma MyCarIsOnFireException, quero saber imediatamente! Posso não me importar com o resto das exceções. Portanto, você deve ter uma pilha de manipuladores de exceção que digam "uau uau, algo está errado aqui" para algumas exceções (relançar, ou capturar e relançar uma nova exceção que explica melhor o que aconteceu) e apenas retornar falso para os outros.
Se este é um produto que você lançará, você deve registrar essas exceções em algum lugar, isso o ajudará a ajustar as coisas no futuro.
Edit: Quanto à questão de envolver tudo em um try / catch, acho que a resposta é sim. As exceções devem ser tão raras em seu código que o código no bloco catch é executado tão raramente que nem atinge o desempenho. Uma exceção deve ser um estado em que sua máquina de estado quebrou e não sabe o que fazer. Pelo menos relançar uma exceção que explica o que estava acontecendo no momento e tem a exceção capturada dentro dela. "Exceção no método doSomeStuff ()" não é muito útil para quem precisa descobrir por que ele quebrou durante as férias (ou em um novo emprego).
fonte
Minha estratégia:
Se a função original retornou void, eu altero para retornar bool . Se ocorrer uma exceção / erro, retorne falso , se tudo estiver bem, retorne verdadeiro .
Se a função deve retornar algo, quando a exceção / erro ocorrer, retorne nulo , caso contrário, o item retornável.
Em vez de bool, uma string pode ser retornada contendo a descrição do erro.
Em todos os casos, antes de retornar qualquer coisa, registre o erro.
fonte
Algumas respostas excelentes aqui. Eu gostaria de acrescentar que se você acabar com algo como postou, pelo menos imprima mais do que o rastreamento da pilha. Diga o que você estava fazendo no momento e Ex.getMessage (), para dar ao desenvolvedor uma chance de lutar.
fonte
Os blocos try / catch formam um segundo conjunto de lógica embutido no primeiro (principal) conjunto, como tal, eles são uma ótima maneira de eliminar códigos ilegíveis e difíceis de depurar.
Ainda assim, usados razoavelmente, eles fazem maravilhas na legibilidade, mas você deve seguir duas regras simples:
use-os (com moderação) no nível inferior para detectar problemas de manipulação da biblioteca e transmiti-los de volta para o fluxo lógico principal. A maior parte do tratamento de erros que desejamos deve vir do próprio código, como parte dos próprios dados. Por que fazer condições especiais, se os dados de retorno não são especiais?
use um grande manipulador no nível superior para gerenciar qualquer ou todas as condições estranhas que surgem no código que não são detectadas em um nível inferior. Faça algo útil com os erros (logs, reinicializações, recuperações, etc).
Além desses dois tipos de tratamento de erros, todo o resto do código no meio deve estar livre e livre de código try / catch e objetos de erro. Dessa forma, ele funciona de maneira simples e esperada, não importa onde você o use ou o que faça com ele.
Paulo.
fonte
Posso chegar um pouco atrasado na resposta, mas o tratamento de erros é algo que sempre podemos mudar e evoluir ao longo do tempo. Se você quiser ler algo mais sobre esse assunto, escrevi um post no meu novo blog sobre o assunto. http://taoofdevelopment.wordpress.com
Boa codificação.
fonte