Recomendar um padrão de design / abordagem para expor / tolerar / recuperar de erros do sistema, tratamento de exceções (por exemplo, em Java, C ++, Perl, PHP)

13

Você pode recomendar um padrão de design / abordagem para expor / tolerar / recuperar de erros do sistema, tratamento de exceções (Java, C ++, Perl, PHP)?

Alguns erros precisam ser relatados.

Alguns erros podem ser tratados internamente (por uma nova tentativa ou são irrelevantes (podem ser ignorados).

Como você estrutura o código para capturá-los?

Mas todos os erros precisam ser registrados.

Quais práticas recomendadas existem?

E para simulá-los para poder testar completamente os componentes que são impactados por eles?

Pergunta geral específica da linguagem não de programação aplicável a várias linguagens de programação modernas, mas gostaria de receber exemplos de ilustrações de padrões, abordagens e filosofias em Java, C ++, PHP e Perl.

(Também solicitado no stackoverflow: /programming/7432596/recommend-a-design-pattern-approach-to-exposing-tolerating-recovering-from-system, mas achei que também deveria ser solicitado aos programadores porque Acho que as perguntas e respostas dos programadores abordam questões mais amplas de software / programação, enquanto o stackoverflow é mais sobre a implementação técnica IMHO).

therobyouknow
fonte
2
Para mim, sua pergunta parece muito geral e aberta para ser respondida com eficácia. Tente restringi-lo a alguns casos mais específicos e também a tipos / ambientes de aplicativos específicos (por exemplo, GUI app / server / ...).
Péter Török
Veja também stackoverflow.com/questions/937285/…
Péter Török
@ Péter Török, mikera fornece uma boa resposta, provavelmente aceita a deles.
therobyouknow 19/09/11

Respostas:

16

Falhar rápido é uma ótima abordagem de design e talvez possa ser contada como um padrão: http://en.wikipedia.org/wiki/Fail-fast

Também achei vários princípios úteis:

  • Considere as exceções como parte da interface para cada função / módulo - ou seja, documente-as e, quando apropriado / se o seu idioma suportar, use as exceções verificadas.
  • Nunca falsifique uma falha - se ela falhar, não tente continuar com alguma tentativa de "assumir" como proceder. Por exemplo, o tratamento especial para casos nulos geralmente é um cheiro de código para mim: se seu método precisar de um valor não nulo, ele deve lançar uma exceção imediatamente se encontrar um nulo (NullPointerException ou, idealmente, uma IllegalArgumentException mais descritiva).
  • Escreva testes de unidade para casos excepcionais e normais - algumas vezes, esses testes podem ser difíceis de configurar, mas vale a pena quando você quer ter certeza de que seu sistema é robusto a falhas
  • Faça logon no ponto em que o erro foi detectado e tratado (supondo que o erro tenha gravidade suficiente para ser registrado). A razão para isso é que isso implica que você entendeu a causa e teve uma abordagem para lidar com o erro, para que você possa tornar a mensagem de log significativa ...
  • Use exceções apenas para condições / falhas verdadeiramente inesperadas. Se sua função "falhar" da maneira esperada (por exemplo, pesquisa para ver se há mais entradas disponíveis e não encontrar nenhuma), ela deverá retornar uma resposta normal ("nenhuma entrada disponível"), não lançar uma exceção
  • Falha normalmente (crédito a Steven Lowe!) - limpe antes de finalizar, se possível, normalmente desenrolando alterações, revertendo transações ou liberando recursos em uma declaração "finalmente" ou equivalente. A limpeza deve ocorrer idealmente no mesmo nível em que os recursos foram comprometidos por uma questão de clareza e consistência lógica.
  • Se você tiver que falhar, falhe alto - uma exceção que nenhuma parte do seu código foi capaz de lidar (ou seja, percolada para o nível superior sem ser capturada + manipulada) deve causar uma falha imediata, alta e visível que direciona sua atenção para ele. Normalmente paro o programa ou a tarefa e escrevo um relatório completo de exceções no System.out.
Mikera
fonte
1
também falham normalmente - limpe antes de terminar se possível
Steven A. Lowe
Marque +1 na @mikera para obter pontos claros sobre o que considerar. Resposta provavelmente aceita, deixarei em aberto um pouco mais para que outros contribuam.
therobyouknow 19/09/11
+1 @ Steve A. Lowe - para design centrado no usuário. por exemplo, como salvar arquivos, não deixar arquivos temporários por aí. Veja minha pergunta sobre "higiene de dados" como um tópico relacionado na minha lista de perguntas.
therobyouknow 19/09/11
5

Tendo trabalhado com exceções em Java e .NET E depois de ler muitos artigos sobre como / quando / por que capturar exceções, finalmente cheguei às etapas a seguir que passo em minha cabeça sempre que vejo uma possível exceção acontecendo, ou um exceção eu devo pegar (Java) ... mesmo que isso nunca aconteça (suspiro ...). E parece estar funcionando, pelo menos para mim:

  1. Existe algo útil que eu possa fazer com essa exceção (exceto o log)? Se a resposta for sim, escreva o código da solução alternativa e, se a solução alternativa gerar exceções, vá para 2:
  2. Envolva a exceção em torno de uma exceção de tempo de execução, ative-a e vá para 3.
  3. Na classe de nível superior em que uma possível transação de banco de dados / processo foi iniciada, capture a exceção, faça a reversão da transação e repita a exceção.
  4. Na classe de nível superior (que pode ser aquela em que a transação foi iniciada), registre a exceção usando uma estrutura de log como slf4j (juntamente com log4j por exemplo) ou log4net . Se possível, envie diretamente a exceção por email para uma lista de distribuição composta por desenvolvedores do aplicativo.
  5. Se houver uma GUI, exiba uma mensagem de erro indicando da maneira mais amigável ao usuário o que causou o problema; não exibe a exceção / rastreamento de pilha, o usuário não se importa e não precisa saber que era uma NullPointerException.

Devo também adicionar a etapa 0 , onde propositalmente estou lançando o que chamo de exceção "comercial" (uma nova exceção criada ao estender a classe "Exception") quando algum tratamento complexo não pode ser executado devido a erros de dados, MAS é conhecido por acontecer, pois foram identificados como casos de exceção durante a análise.

Exceto pela parte de registro, concordo plenamente com os pontos escritos por "mikera"; Vou apenas acrescentar que a exceção deve ser registrada apenas uma vez .

Além disso, as etapas listadas podem ser diferentes se o que você está escrevendo é uma API / Framework . Lá, lançar exceções bem projetadas é obrigatório para ajudar os desenvolvedores a entender seus erros.

Quanto ao teste das exceções, usando objetos simulados, você deve poder testar quase tudo, seja ele excepcional ou não, desde que suas classes respeitem a melhor prática "uma classe para fazer uma coisa". Pessoalmente, também certifico-me de marcar os métodos mais importantes, mas ocultos, como "protegidos" em vez de "privados", para que eu possa testá-los sem muito trabalho. Além disso, testar exceções é simples, apenas provoque a exceção e "espere" que uma exceção ocorra capturando-a. Se você não receber uma exceção, terá um erro de caso de teste de unidade.

Jalayn
fonte
+1 em @Jalayn pela menção do log4j como uma abordagem, é isso que eu uso, então isso reforça o fato de sabermos que outra pessoa também é.
therobyouknow 19/09/11
0

Crie seus objetos da maneira certa, não se preocupe com fatores externos. Se você optar por tirar proveito das exceções, faça seus objetos lançarem exceções se eles falharem em algo.

Depois que todos os seus objetos estiverem funcionando corretamente, deve ser bastante fácil criar uma hierarquia de responsabilidade limpa no tratamento de erros em seu design.

Yam Marcovic
fonte