Estou escrevendo um pequeno programa simples para transmitir MIDI através de uma rede. Sei que o programa encontrará problemas de transmissão e / ou outras situações de exceção que não poderei prever.
Para o tratamento de exceções, vejo duas abordagens. Devo escrever o programa para que:
- falha com um estrondo quando algo dá errado ou
- deveria apenas ignorar o erro e continuar, à custa da integridade dos dados?
Qual abordagem um usuário esperaria razoavelmente?
Existe uma maneira melhor de lidar com exceções?
Além disso, minha decisão sobre o tratamento de exceções deve ser afetada pelo fato de eu estar ou não lidando com uma conexão de rede (por exemplo, algo em que posso razoavelmente esperar que ocorram problemas)?
exception-handling
Arlen Beiler
fonte
fonte
Respostas:
Nunca, você nunca deve ignorar um erro que seu programa encontra. No mínimo, você deve registrá-lo em um arquivo ou em algum outro mecanismo para notificação. Não pode haver situações ocasionais em que você vai querer ignorar um erro, mas documentá-lo! Não escreva um
catch
bloco vazio sem comentários que expliquem por que está vazio.Se o programa falhará ou não, depende muito do contexto. Se você conseguir lidar com o erro normalmente, vá em frente. Se for um erro inesperado, seu programa falhará. Isso é praticamente o básico do tratamento de exceções.
fonte
Você nunca deve ignorar silenciosamente os erros, porque seu programa é desenvolvido com base em uma série de ações que dependem implicitamente de tudo o que aconteceu antes que elas dêem certo. Se algo der errado na etapa 3 e você tentar continuar na etapa 4, a etapa 4 começará com base em suposições inválidas, o que aumenta a probabilidade de que também acabe gerando um erro. (E se você ignorar isso também, a etapa 5 gera um erro e as coisas começam a bola de neve a partir daí.)
O problema é que, à medida que os erros se acumulam, você eventualmente cometerá um erro tão grande que não poderá ignorá-lo, porque consistirá em algo que está sendo dado ao usuário e que algo estará completamente errado. Então você tem usuários reclamando de que seu programa não está funcionando corretamente e você precisa corrigi-lo. E se a parte "fornecer algo ao usuário" estiver na etapa 28 e você não tiver idéia de que o erro original que está causando toda essa bagunça estava na etapa 3, porque você ignorou o erro na etapa 3, terá uma muito tempo depurando o problema!
Por outro lado, se esse erro na etapa 3 fizer tudo explodir na cara do usuário e gerar um erro dizendo
SOMETHING WENT BADLY WRONG IN STEP 3!
(ou seu equivalente técnico, um rastreamento de pilha), o resultado será o mesmo - o usuário reclamando de você o programa não está funcionando corretamente - mas desta vez você sabe exatamente por onde começar a procurar quando for corrigi-lo .EDIT: Em resposta aos comentários, se algo der errado que você antecipou e sabe como lidar, isso é diferente. Por exemplo, no caso de receber uma mensagem incorreta, isso não é um erro de programa; isso é "o usuário forneceu informações incorretas que falharam na validação". A resposta apropriada é informar ao usuário que ele está fornecendo informações inválidas, e é isso que parece que você está fazendo. Não há necessidade de travar e gerar um rastreamento de pilha em um caso como esse.
fonte
Existem outras opções entre "explodir" e "ignorar".
Se o erro for previsível e evitável, altere seu design ou refatore seu código para evitá-lo.
Se o erro for previsível, mas não evitável, mas você souber o que fazer quando isso acontecer, identifique o erro e lide com a situação. Mas tenha cuidado para evitar o uso de exceções como controle de fluxo. E você pode registrar um aviso neste momento e talvez notificar o usuário se houver alguma ação que ele possa tomar para evitar essa situação no futuro.
Se o erro for previsível, inevitável e, quando isso acontecer, não haverá nada que garanta a integridade dos dados, será necessário registrar o erro e retornar a um estado seguro (o que, como já foi dito, pode significar falha).
Se o erro não é algo que você previu, então você realmente não pode ter certeza de que pode voltar a um estado seguro; portanto, é melhor apenas registrar e travar.
Como regra geral, não pegue nenhuma exceção sobre a qual você não possa fazer nada, a menos que planeje registrá-la e reproduzi-la novamente. E nos raros casos em que um try-catch-ignore é inevitável, adicione pelo menos um comentário no bloco catch para explicar o porquê.
Consulte o excelente artigo sobre tratamento de exceções de Eric Lippert para obter mais sugestões sobre como categorizar e manipular exceções.
fonte
Estes são os meus pontos de vista sobre a questão:
Um bom princípio de partida é falhar rapidamente. Especificamente, você nunca deve escrever um código de manipulação de erros para qualquer falha cuja causa não seja conhecida.
Depois de aplicar esse princípio, você pode adicionar o código de recuperação para condições de erro específicas encontradas. Você também pode introduzir vários "estados seguros" para retornar. A interrupção de um programa é quase sempre segura, mas às vezes você pode querer retornar a outro bom estado conhecido. Um exemplo é como um sistema operacional moderno lida com um programa incorreto. Ele apenas desliga o programa, não todo o sistema operacional.
Ao falhar rápida e lentamente, cobrindo condições de erro cada vez mais específicas, você nunca compromete a integridade dos dados e avança constantemente para um programa mais estável.
Erros de deglutição, ou seja, tentar planejar erros para os quais você não conhece a causa exata e, portanto, não possui uma estratégia de recuperação específica, apenas leva a uma quantidade crescente de códigos de pular e contornar erros no seu programa. Como não se pode confiar que os dados anteriores foram processados corretamente, você começará a verificações dispersas de dados incorretos ou ausentes. Sua complexidade ciclomática ficará fora de controle e você terminará com uma grande bola de lama.
Se você está ciente ou não dos casos de falha é menos importante. Mas se você estiver lidando, por exemplo, com uma conexão de rede para a qual conhece uma certa quantidade de estados de erro, adie a adição de tratamento de erros até também adicionar código de recuperação. Isso está de acordo com os princípios descritos acima.
fonte
Você nunca deve ignorar silenciosamente os erros. E, especialmente, não à custa da integridade dos dados .
O programa está tentando fazer alguma coisa. Se falhar, você deve encarar o fato e fazer algo a respeito. O que essa coisa será depende de muitas coisas.
No final, o usuário solicitou que o programa fizesse alguma coisa e o programa deveria dizer a eles que não teve êxito. Há muitas maneiras de como fazer isso. Pode parar imediatamente, pode até reverter as etapas já concluídas ou, por outro lado, pode continuar e concluir todas as etapas possíveis e depois informar ao usuário que essas etapas foram bem-sucedidas e as demais falharam.
O caminho que você escolher depende da proximidade entre as etapas e se é provável que o erro ocorra novamente em todas as etapas futuras, o que, por sua vez, pode depender do erro exato. Se for necessária uma integridade forte dos dados, você precisará reverter para o último estado consistente. Se você está apenas copiando vários arquivos, pode pular alguns e dizer ao usuário no final que esses arquivos não puderam ser copiados. Você não deve pular arquivos silenciosamente e não dizer nada ao usuário.
Edição de anúncio, a única diferença que faz é que você deve tentar repetir várias vezes antes de desistir e dizer ao usuário que não funcionou, pois é provável que a rede tenha erros transitórios que não ocorrerão novamente se você tentar novamente.
fonte
Há uma classe de casos em que ignorar erros é a coisa certa a fazer: quando não há nada que possa ser feito sobre a situação e quando resultados ruins e possivelmente incorretos são melhores que nenhum resultado.
O caso da decodificação do fluxo HDMI para fins de exibição é esse. Se o fluxo é ruim, é ruim, gritar sobre isso não o corrigirá magicamente. Você faz o possível para exibi-lo e deixa o espectador decidir se é tolerável ou não.
fonte
Não acredito que um programa silenciosamente ignore ou cause estragos sempre que ocorrer um problema.
O que faço com o software interno que escrevo para minha empresa ...
Depende do erro, digamos que se é uma função crítica que está inserindo dados no MySQL, ele precisa informar ao usuário que falhou. O manipulador de erros deve tentar coletar o máximo de informações e fornecer ao usuário uma idéia de como corrigir o erro para que eles possam salvar os dados. Também gosto de fornecer uma maneira silenciosa de nos enviar as informações que eles tentam salvar, para que, se piorar, possamos inseri-la manualmente após a correção do bug.
Se não for uma função crítica, algo que pode errar e não afetar o resultado final do que eles estão tentando alcançar, talvez eu não mostre uma mensagem de erro, mas envie um email que o insira automaticamente em nosso software de rastreamento de bugs ou um grupo de distribuição de e-mail que alerta todos os programadores da empresa para que tomemos conhecimento do erro, mesmo que o usuário não esteja. Isso nos permite corrigir o back-end enquanto no front-end ninguém sabe o que está acontecendo.
Uma das maiores coisas que tento evitar é travar o programa após o erro - não conseguir recuperar. Eu sempre tento dar ao usuário a opção de continuar sem fechar o aplicativo.
Acredito que se ninguém souber sobre o bug - ele nunca será corrigido. Também acredito firmemente no tratamento de erros que permite que o aplicativo continue funcionando depois que um bug for descoberto.
Se o erro estiver relacionado à rede - por que não fazer com que as funções executem um teste simples de comunicação de rede antes de executar a função para evitar o erro em primeiro lugar? Em seguida, basta alertar o usuário que uma conexão não está disponível, verifique sua internet etc. etc. e tente novamente?
fonte
Minha própria estratégia é distinguir entre erros de codificação (bugs) e erros de tempo de execução e, tanto quanto possível, dificultar a criação de erros de codificação.
Os erros precisam ser corrigidos o mais rápido possível, para que uma abordagem de Design por contrato seja apropriada. No C ++, gosto de verificar todas as minhas pré-condições (entradas) com asserções na parte superior da função para detectar o bug o mais rápido possível e facilitar a conexão de um depurador e a correção do bug. Se o desenvolvedor ou o testador optar por tentar continuar executando o programa, qualquer perda de integridade dos dados se tornará problema deles.
E encontre maneiras de evitar o bug em primeiro lugar. Ser rigoroso com a correção constante e escolher tipos de dados apropriados para os dados que eles conterão são duas maneiras de dificultar a criação de bugs. O Fail-Fast também é bom fora do código crítico de segurança que precisa de uma maneira de se recuperar.
Para erros de tempo de execução que podem ocorrer com código sem erros, como falhas de comunicação em rede ou serial ou arquivos ausentes ou corrompidos:
fonte
Falhar é a opção certa quando você tem motivos para pensar que o estado geral do programa é instável e algo ruim pode acontecer se você deixar que ele seja executado a partir de agora. Um pouco "ignorá-lo" (ou seja, como outros já apontaram, registrando-o em algum lugar ou exibindo uma mensagem de erro para o usuário e continuando) está ok quando você sabe que, com certeza, a operação atual não pode ser executada, mas o programa pode continue correndo.
fonte