Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Porém, em muitos casos, os tratamentos de exceções não são elaborados e utilizados como supostamente deveriam ser. Abaixo

Nesta documentação algumas premissas relacionadas à exceções no código e algumas dicas para empregar o tratamento. Page Treeroot@selfexpandCollapseAlltruespacesDPUstartDepth1searchBoxtrue

1 - Escreva o esqueleto do bloco antes de codificar

Em primeiro lugar, uma dica importante: quando você Quando iniciar um bloco de tratamento de exceções, escreva todo toda 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:

Code Block

...

languagepas
uses
  Classes, System, SysUtils;

var  
  Objeto: TStringList;
  
begin  
  Objeto := 

...

TStringList.Create;

...

  
  try   
    Objeto.Add('Documentação boas práticas de codificação');
  finally  
    Objeto.Free; 
  end;  
end;

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”.

2 - 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:

Code Block

...

languagepas
try  
  ConsultarPorCodigo;
except  
  ConsultarPorDescricao;
end;

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

Code Block
languagepas
var

...

 

...

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:

Code Block

123456789try  StrToInt('A');except  On E: Exception do    ShowMessage(      'Ocorreu um erro.' + #13 +      'Por favor, entre em contato com o administrador do sistema.' + #13 +      'Mensagem de erro: ' + E.Message);end;

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:

Code Block

123456789101112try  StrToInt('A');except  On E: Exception do  begin    ShowMessage(      'Ocorreu um erro.' + #13 +      'Por favor, entre em contato com o administrador do sistema.');     GravarErroNoArquivoDeLog(E.Message);  end;end;

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

Code Block

123456789101112131415161718192021procedure GravarErroNoArquivoDeLog(const sMensagem: string);var  sCaminhoLogErros: string;  ArquivoLog: TextFile;begin  sCaminhoLogErros := 'C:\LogErros.txt';  AssignFile(ArquivoLog, sCaminhoLogErros);   // se o arquivo já existir, será aberto para modificação  // caso contrário, o arquivo será criado  if FileExists(sCaminhoLogErros) then    Append(ArquivoLog)  else    Rewrite(ArquivoLog);   WriteLn(ArquivoLog, 'Data: ' + DateTimeToStr(Now)); // escreve a data e hora  WriteLn(ArquivoLog, 'Mensagem: ' + sMensagem); // escreve a mensagem  WriteLn(ArquivoLog, EmptyStr); // pula uma linha   CloseFile(ArquivoLog);end;

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:

Code Block

12345678try  StrToInt(VariavelX); // gera uma exceção  GravarCliente;  StrToInt(VariavelY); // gera uma exceçãoexcept  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 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:

Code Block

12type  EMinhaExcecao = class(Exception);

Para utilizá-las, basta chamar o raise:

Code Block

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

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:

Code Block

123456789101112131415161718192021222324252627282930313233343536373839type  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!

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:

Code Block

1234567891011121314151617181920212223242526private  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:

Code Block

12345678910111213141516procedure 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 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.

...

 vTipoConsulta: String;
  
begin   
  try  
    if vTipoConsulta = 'Codigo' then    
      ConsultarPorCodigo  
    else if vTipoConsulta = 'Descricao' then
      ConsultarPorDescricao;
  except  
    ShowMessage('Erro ao realizar a consulta.');
  end;
end;

Abaixo outros exemplos de como utilizar isso no VsScripter de maneira correta e algumas dicas mais específicas para cada caso.

Page Tree
root@self
expandCollapseAlltrue
spacesDPU
startDepth1
searchBoxtrue