Técnicas de tratamento de exceções

ppersWraMuito se fala sobre tratamento de exceções no desenvolvimento de software. Estes tratamentos são extremamente úteis para controlar a fluxo de execução do aplicativo quando algum erro ocorre, bem como servir como um bom recurso de rastreabilidade. Porém, em muitos casos, os tratamentos de exceções não são elaborados e utilizados como supostamente deveriam ser. Por esse motivo, o artigo de hoje apresenta algumas premissas relacionadas à exceções no código e algumas dicas para empregar o tratamento.

Antes de iniciar o artigo, vale realçar que a palavra reservada except do Delphi que utilizarei nos exemplos é o mesmo que o catch de outras linguagens, como Java e C#, portanto, a ideia é a mesma.

Escreva o esqueleto do bloco antes de codificar

Em primeiro lugar, uma dica importante: quando você iniciar um bloco de tratamento de exceções, escreva todo a estrutura do tratamento antes de continuar a codificação. Essa dica é ainda mais importante para instruções finally, já que nos impede de esquecer de liberar um objeto da memória. Por exemplo, caso seja necessário instanciar um objeto em um método, a estrutura pode ser escrita dessa forma:

Somente depois de escrever essa estrutura que a codificação dentro do try deve ser iniciada. Da mesma forma, se um objeto da classe estiver sendo instanciado no evento OnCreate de um formulário, o código para destruí-lo deve ser imediatamente escrito no evento OnDestroy. Se isso não for feito, haverá Memory Leaks na aplicação, comprometendo o desempenho e aumentando a probabilidade de erros consecutivos, também conhecidos como “efeito cascata”.

Use o except exclusivamente para tratamento de exceções

Em segundo lugar, é preciso entender que o except não deve ser empregado como uma condição If. Alguns desenvolvedores pensam da seguinte forma: “Vou tentar executar o plano A (try), e se não der certo, executo o plano B (except)”. Isso não existe. O tratamento de exceções, como o próprio nome diz, deve ser utilizado apenas para exceções, e não para regras de negócio.

Como exemplo, considere o código:

O bloco try/except está sendo incorretamente utilizado como uma condição para atender uma funcionalidade. Este código deve ser imeidatamente corrigido com uma condição If, utilizando, por exemplo, um tipo enumerado, dispensando o except de tratar regras de negócio:

Faça uso da propriedade Message da classe Exception

A terceira recomendação é utilizar a propriedade Message da classe Exception para apresentar o erro técnico na tela. Claro, o usuário provavelmente não entenderá a descrição da mensagem, mas, quando reportada, será muito útil para ajudar a equipe de suporte, ou até mesmo os desenvolvedores, a identificar a origem do erro. No código abaixo, a mensagem de erro é capturada e exibida na tela:

A mensagem de erro será: ‘A’ is not a valid integer value. Essa descrição servirá de orientação para identificar quando ou como o erro foi produzido, agilizando o processo de manutenção.

Por outro lado, para evitar o “estouro” de erros técnicos na tela do usuário, outra alternativa é gravar a mensagem de erro em um arquivo de log, na qual considero bastante viável. Neste caso, basta substituir a última linha da mensagem de erro por um comando que acesse um arquivo de texto e grave a descrição:

O método GravarErroNoArquivoDeLog, por sua vez, pode executar a seguinte operação:

Legal, não é?

Exceções personalizadas

Prosseguindo, uma ótima dica para melhorar o tratamento de exceções é não utilizar exceções genéricas. Por exemplo, considere o tratamento abaixo:

Embora a mensagem de erro seja bem explicativa, não saberemos exatamente se a exceção ocorreu no primeiro ou no segundo ponto do código, logo, também não saberemos se o método GravarCliente foi executado. Isso acontece porque estamos utilizando a classe genérica de exceções, ou seja, Exception, que não nos permite identificar o ponto em que a exceção ocorreu.

Além disso, não conseguimos aplicar tratamentos específicos se o erro for genérico. Para melhorar a compreensão, suponha que no except há um método para limpar alguns campos da tela caso a exceção tenha sido causada por uma conversão de dados inválida. Mas, e se ocorrer outro tipo de exceção? Imagine, por exemplo, que ocorra uma exceção ao gravar um registro no DataSet. O fluxo irá para o except e executar o método que limpa os campos, indevidamente.

O ideal é criar exceções personalizadas, principalmente para tratar regras de negócio. No Delphi, é possível elaborar esses tipos de exceções ao criar heranças da classe Exception:

Para utilizá-las, basta chamar o raise:

A principal vantagem dessa abordagem é separar diferentes exceções que podem ocorrer dentro de um bloco try e coordenar o fluxo de acordo com o tipo da exceção. Como exemplo, considere um determinado método que pode gerar três tipos de exceções. Se utilizarmos exceções personalizadas, observe como o tratamento fica bem mais claro:

Conforme o tipo de exceção, realizamos o tratamento adequado, o que não seria possível com exceções genéricas. Além disso, caso ocorra uma exceção para o usuário em ambiente de produção, a rastreabilidade será mais rápida!

Wrappers

Se o tamanho do código no except for um problema, existe uma técnica conhecida como Wrapper, que consiste em encapsular o tratamento de exceção dentro de um método separado. No cenário do exemplo acima, o Wrapper ficaria dessa forma:

Logo, o código inicial seria reduzido:

Observe que, neste caso, a classe Exception foi utilizada para capturar a exceção de forma geral, mas é tratada dentro do Wrapper empregando RTTI. A vantagem do Wrapper, além do encapsulamento, é a possibilidade de utilizá-lo em vários pontos do software que compartilham as mesmas regras de exceção, como, por exemplo, a exceção de campos obrigatórios, que pode ser aplicada em telas distintas.

Antes de fechar o artigo, mais uma dica rápida. O componente TApplicationEvents traz o evento OnException que faz justamente a função do Wrapper. O benefício deste componente é capturar todas as exceções que ocorrem dentro do software, independente da tela que estiver aberta.

 

Bom, pessoal, por hoje é só! Espero que as dicas acima tenham sido produtivas!
Há também um artigo aqui no blog sobre como criar uma rotina básica de captura de exceções. Confira!

Até a próxima semana!


André Celestino