Técnicas de tratamento de exceções – Parte 2

Técnicas de tratamento de exceções - Parte 2Olá, pessoal! Na semana passada, abordei algumas orientações e técnicas de tratamento de exceções, envolvendo estruturas, condições e a propriedade Message da classe Exception. Conforme prometido, hoje continuo este tema e apresento mais algumas dicas interessantes, ligeiramente voltada para a Orientação a Objetos.

 

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

try
  StrToInt(CaracterX); // gera uma exceção
  GravarCliente;
  StrToInt(CaracterY); // gera uma exceção
except
  On E: Exception do
    ShowMessage(E.Message);
end;

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 local onde a exceção ocorreu.
Além disso, não conseguimos conduzir tratamentos isolados conforme o erro genérico capturado pela classe Exception. 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 o que existe lá? Ops, o método que limpa os campos! Ele será executado indevidamente!

O ideal é criar exceções coordenadas ou 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:

type
  EMinhaExcecao = class(Exception);

Para utilizá-las, basta chamar o raise:

raise EMinhaExcecao.Create('Mensagem da classe EMinhaExcecao.');

 

Qual a vantagem?
A principal vantagem é separar diferentes exceções que podem ocorrer dentro de um bloco try e coordenar o tratamento 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:

type
  ECamposObrigatorios = class(Exception);
  EErroConexao = class(Exception);
  EErroGravacao = class(Exception);
  ...
 
procedure BotaoGravarClick;
begin
  try
    if not (ValidarCamposObrigatorios) then
      raise ECamposObrigatorios.Create('Campos obrigatórios não preenchidos.');
 
    if not (ConectarBancoDeDados) then
      raise EErroConexao.Create('Erro ao conectar ao banco de dados.');
 
    if not (GravarCliente) then
      raise EErroGravacao.Create('Erro ao gravar os dados.');
  except
    On E:ECamposObrigatorios do
    begin
      ShowMessage(E.Message);
      DestacarCamposObrigatorios; // tratamento personalizado
    end;
 
    On E:EErroConexao do
    begin
      ShowMessage(E.Message);
      CancelarGravacao; // tratamento personalizado
      ReconectarBancoDeDados; // tratamento personalizado
    end;
 
    On E:EErroGravacao do
    begin
      ShowMessage(E.Message);
      Transacao.RollBack; // tratamento personalizado
      GravarErroNoLog(E.Message); // tratamento personalizado
    end;
  end;
end;

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!

 

Mas o código ficou muito grande!
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:

private
  procedure TratarExcecao(Excecao: Exception); // declaração do wrapper
  ...
 
procedure TratarExcecao(Excecao: Exception);
begin
  if Excecao is ECamposObrigatorios then
  begin
    ShowMessage(Excecao.Message);
    DestacarCamposObrigatorios; // tratamento personalizado
  end;
 
  if Excecao is EErroConexao then
  begin
    ShowMessage(Excecao.Message);
    CancelarGravacao; // tratamento personalizado
    ReconectarBancoDeDados; // tratamento personalizado
  end;
 
  if Excecao is EErroGravacao then
  begin
    ShowMessage(Excecao.Message);
    Transacao.RollBack; // tratamento personalizado
    GravarErroNoLog(Excecao.Message); // tratamento personalizado
  end;
end;

Logo, o código inicial seria reduzido:

procedure BotaoGravarClick;
begin
  try
    if not (ValidarCamposObrigatorios) then
      raise ECamposObrigatorios.Create('Campos obrigatórios não preenchidos.');
 
    if not (ConectarBancoDeDados) then
      raise EErroConexao.Create('Erro ao conectar ao banco de dados.');
 
    if not (GravarCliente) then
      raise EErroGravacao.Create('Erro ao gravar os dados.');
  except
    On E:Exception do
      TratarExcecao(E);
  end;
end

Observe que, neste caso, a classe Exception teve de ser utilizada para capturar a exceção de forma geral, mas foi tratada no 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.

 

Pessoal, espero que tenham curtido o artigo!
Até a próxima!


Confira as outras partes deste artigo:

Técnicas de tratamento de exceções – Parte 1
Técnicas de tratamento de exceções – Parte 2


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.