O tratamento de erro é um recurso muito importante, tanto para o usuário como para nós desenvolvedores. Mas não basta só tratar o erro, temos que tratar o erro com elegância e estilo.
Normalmente temos o costume de criar flags de erro ou retornar código para que o chamador possa verificar, como o código abaixo ilustra.
procedure Logar
begin
if (FUsuario.Login <> Emptystr) then
begin
ValidaUsuario(FUsuario);
if (FUsuario.Ativo) then
begin
GravaSessionUsuario(FUsuario);
CarregaConfiguracao(FUsuario);
end
else
begin
Showmensagem(" Usuario nao esta Ativo)
end
end
else
Showmensagem(" Login em branco ")
end.
O problema desta abordagem que nossa função "logar" que acumula responsabilidade e não deixa o chamador tomar uma decisão sobre o erro lançado para o usuário, refactorando para uma bordagem "código limpo" podemos fazer da seguinte maneira:
procedure logar;
begin
Try
doLogar
except on E: EControlUsuario do
ShowMessage(E.Message);
end;
end;
procedure doLocar;
begin
ValidaUsuario(FUsuario);
GravaSessionUsuario(FUsuario);
CarregaConfiguracao(FUsuario);
end;
Bem, como podemos observar o código fica mais claro, isso porque as duas preocupações que estavam no primeiro código, o algoritmo "validar usuário" e do tratamento de erro agora estão separadas.
O controle de Exceção podem definir o escopo dentro de um programa, ao executar o código na parte try da estruda try ...except...Finally assim você pode controlar como se fosse uma transação. Mas lembra-se que uma boa prática e deixar dentro da estrutura try... except... finally antes de lançar uma exceção.
Sempre forneça exceção com um contexto para que possa facilmente o localizar no seu fonte. Um erro ou atá mesmo pegar a stack trace de qualquer exceção. Crie mensagem de erro informativas e passe juntamente com as exceções e sempre que possível mencione a operação que falhou e o tipo da falha.
Define as classes de exceções segundo as necessidades do chamador, ao invez de usar exceções genéricas. Um exemplo de uma classificação ruim de exceção: aqui há uma estrutura de try... except... finally para uma chamada a um componente de terceiro, ela cobre todas as exceções que a chamada talvez lance
ABoleto:= TBoleto.create(ADados);
try
ABoleto.Gerar;
except
on EConversionError
logar(E.Message)
ShowMessage(E.ClassName+'' Nao e possivel converter valores: ''+E.Message);
on EZeroDivide
logar(E.Message)
ShowMessage(E.ClassName+'': Verifique os tryvalores ''+E.Message);
...
finally
....
end;
A estrutura acima possui muitas duplicações, e na maioria dos casos de tratamento de exceções fazemos assim: tratando exceções por exceções de chamadas de componentes de terceiros, uma boa prática é encapsular a chamada e garantir que retorne um tipo comum de exceções.
try
ABoleto := WRBoleto.create(ADados);
try{
Aboleto.gerar;
except
logar(E.Message)
ShowMessage(E.Message);
end
finally
....
end;
Nossa WRBoleto é um simples wrapper que captura e traduz as exceções lançadas pela :
type
EWRBoleto : Exception;
WRBoleto = class
FBoleto :TBoleto
....
procedure Gerar;
...
......
procedure WRBoleto.Gerar;
begin
FBoleto.Gerar;
except
on EConversionError
raise EWRBoleto.Create(E.Message);
on EZeroDivide
raise EWRBoleto.Create(E.Message);
end;
Dessa maneira de empacotar componentes de terceiros, minimiza as dependências delas, você pode escolher migrar para um outro componente diferente no futuro sem muitos problemas.
E para finalizar, no livro Codigo Limpo Robert, dá duas dicas muito importantes que podem levar erros "Não retorne Null " e "Não passe Null", que deixa para você leitor, dá uma espiada nessas dicas importantes.