Exceções, lançar ou não lançar, eis a questão!

Olá galera!

Neste post quero abordar o Tema de Exceções (Exceptions) em Java. Além de pertencer ao programa de certificação da linguagem sempre encontramos problemas relacionados às mesma e acreditem, também já vi problemas em C# .Net. Lendo o blog da Dzone, um artigo me chamou a atenção, 5 dicas de melhores práticas ao utilizar Exceções em Java.

O autor Ajitesh Kumar comenta suas experiências ao realizar Code Review (Revisão de Código) em projetos em andamento ou em construção e dá dicas de melhores práticas. Vamos lá!

Nota: Este post é a tradução e algumas observações que farei compartilhando minha experiência nestes 14 anos de Java e 6 anos de C# .Net.

O que deve ser evitado?

  • Exceções dos tipos Throwable e Error não devem ser capturados.
  • Throwable.printStackTrace(…) não deve ser chamado.
  • Exeções Genéricas como: Error, RuntimeException, Throwable e Exception não devem ser lançadas.
  • As mensagens originais dos eventos de exceções devem ser preservadas.
  • System.out ou System.err não deveriam ser utilizados como Loggers.

Exceções Throwable e Error não devem ser capturados

A importância de não capturar estes eventos é que perde-se a exceção original e mascarando a mensagem, pois nenhuma classe, objeto, ou outro desenvolvedor tem como definir exatamente que erro será lançado, portanto isso deixa o código ilegível e confuso. Saiba mais neste <strong>post.

try {
	cl = Thread.currentThread().getContextClassLoader();
}catch (Throwable ex) { // Código incompatível
}

Throwable é a superclasse de todos os erros e exceções em Java.
Error é a superclasse de todos os erros que não são destinadas a ser capturado pelos aplicativos. Assim, a captura de Throwable seria, essencialmente, para capturar erros como exceções do sistema como por exemplo: OutOfMemoryError, StackOverflowError ou InternalError. A abordagem recomendada é: sua aplicação não deve tentar se recuperar de erros como estes. Portanto, as classes Throwable e Error não devem ser capturadas, somente Exception e suas subclasses.

Dica:

Ao implementar uma Exception especializada para o seu sistema defina sua responsabilidade de forma concisa e precisa para ter-se uma mensagem clara em sua aplicação e não herde ou capture Throwable e Error.

Conclusão

Acima, há razões por que as pessoas ainda vão capturar eventos do Throwable (Erros do S.O.). Os erros de dados, tais como problemas de codificação, etc, que não são conhecidos em tempo de programação pode ser capturado usando esta técnica. No entanto, a captura Throwable como InternelError ou OutOfMemoryError não seria de nenhuma ajuda, e não justifica sua utilização. Assim, deve-se evitar escrever código que consiste em pegar Throwable como prática geral de codificação.

No Capítulo 5 do livro para certificação para programador java 6 da Kathy Sierra, página 211, seção Hierarquia de Exceções, ela faz uma observação muito interessante sobre este assunto:

“… há duas subclasses que derivam de Throwable: Exception e Error. As classes que derivam de Error representam situações incomuns que não são causadas por erros no programa e indicam coisas que não ocorreriam normalmente durante sua execução, como a JVM ficar sem espaço na memória. Geralmente aplicativos não conseguem se recuperar de erros como este e por isso não precisamos manipulá-los.”

“Os erros, tecnicamente, não são exceções, pois não derivam da classe Exception.”

Não faça chamada ao método Throwable.printStackTrace (…)

try {
  /* ... */
} catch(Throwable t) {
  t.printStackTrace();// Imcompatível
}

A seguir estão algumas das razões pelas quais deve-se evitar o recurso de chamada ao método printStackTrace de classes Throwable e Exception em vez usar o método Logger usando um dos frameworks, como logback ou Log4J:

  • Difícil de recuperar os logs na depuração: Os registros escritos no console usando printStackTrace é escrito para System.err, ou seja, definido para mensagens de erro de Sistema, que além de ser difícil identificar a rota (Rastreabilidade) ou filtrar em outro lugar. Em vez disso, usando Loggers, é fácil recuperar registros para fins de depuração.
  • Violação das boas práticas recomendadas de codificação: Geralmente, de acordo com diretrizes de codificação em aplicativos em ambiente de produção, os desenvolvedores precisam usar métodos Logger em nível diferente de Info. No entanto, quando se trata de manipulação de exceção, as instâncias de printStackTrace são comumente encontrados em vários lugares. Esta é, portanto, uma violação desta prática e, assim, deve ser evitada.

Exceções Genéricas como Error, RuntimeException, Throwable e Exception nunca devem ser lançadas.

A seguir estão algumas das razões pelas quais Exceções Genéricas ou Throwable nunca devem ser lançados:

  • A principal razão pela qual deve-se evitar gerar exceções genéricas, Throwable, Error, etc, é que fazendo desta forma as classes ficam impedidas de capturar as exceções previstas. Assim, um chamador não pode examinar a exceção para determinar por que ele foi lançado e, consequentemente, não pode tentar a recuperação.
  • Além disso, a captura de RuntimeException é considerada como uma prática ruim. E, assim, gerar exceções genéricas ou Throwable levaria o desenvolvedor a capturar a exceção, numa fase posterior, que acabaria por levar a um código com “mal cheiros”.

Segue-se o exemplo de código que representa esse “mal cheiro” no código:

public void foo(String bar) throws Throwable { // Incompatível
  throw new RuntimeException("Minha Mensagem");// Incompatível
}

// Um outro exemplo que lança uma Exceção
public void doSomething() throws Exception {...} // Código Incompatível

Em vez disso, deveríamos fazer algo como a seguir:

public void foo(String bar) {
  throw new CustomRuntimeException("Minha Mensagem");// Compatível
}

“Manipuladores de exceção deve preservar a exceção original.” – Ajitesh Kumar

Ao escrever código para fazer o tratamento de exceção, muitas vezes tenho visto código como na sequência do qual faz algum dos seguintes procedimentos:

No exemplo de código abaixo, a exceção é perdido.

try {
	/* ... */
} catch( Exception e ) {

	SomeLogger.info( e.getMessage() ); }

No exemplo de código abaixo, objeto de exceção todo está perdido.

try {
 /* ... */
} catch( Exception e ) {
 // A Exceção será perdida.
 SomeLogger.info( "alguma mensagem do contexto" );
}
No exemplo de código abaixo, nenhuma mensagem de contexto é fornecida.
try {
    /* ... */
} catch( Exception e ) {
 SomeLogger.info( e ); // Nenhuma mensagem de contexto
}

Como melhor prática, alguém iria querer fazer algo como se segue:

try {
 /* ... */
} catch( Exception e ) {
 // A mensagem de contexto está aqui. Todavia o objeto de exceção também está presente.
 SomeLogger.info( "alguma mensagem de contexto", e );
}
Em caso de exceções throwable, a seguir deve ser feito:
try {
 /* ... */
} catch (Exception e) {
 //A mensagem de contexto está aqui. Todavia o objeto de exceção também está presente.
 throw new CustomRuntimeException("contexto", e);
}

System.out ou System.err não deve ser usado para exceções log

A principal razão pela qual deve-se evitar o uso de System.out ou System.err para log exceção é o fato de que se pode simplesmente perder as mensagens de erro importantes. Em vez disso, deve-se utilizar os frameworks de log como Log4J ou Logback para registrar as exceções.

Artigo original: http://java.dzone.com/articles/java-top-5-exception-handling

Eu adiciono a este post um problema que frequentemente é encontrado em alguns projetos.

Blocos try/catch não devem ficar sem código

Comumente chamados de blocos silenciados, em alguns projetos pelos quais eu já passei, foi encontrado em inúmeros pontos do sistema blocos try/catch “silenciados”, ou seja, sem código nenhum. O principal problema neste tipo de código é que erros sistêmicos, erros lógicos e grandes falhas ficam incobertas, sem nenhum registro de log aparente, e quando o problema é percebido muito dinheiro poderá ter sido jogado fora, tanto pelo cliente, quanto pela empresa de tecnologia que desenvolveu o sistema. O cliente muitas vezes só vai descobrir que poderá ter um rombo enorme em dados, e até multas fiscais, problemas de processos em seus negócios, perda de históricos, informações que poderiam agregar valor ao seu negócio, e o mais importante, perdendo negócios diretamente com seus clientes.

Portanto ao ler este post fiquei interessado em compartilhar com a comunidade esta pequena discussão em que é importante ter a consciência que existem padrões e técnicas para minimizar estes problemas e fazer da melhor forma é o caminho. Todavia é preciso trilhar este caminho da pesquisa, da leitura, e da prática.

Bons estudos! Bons códigos!

 

Atenciosamente,

Jesus

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s