Explicação
- Exception's deveriam ser usadas para exceções de fato, e não para a logica tradicional da aplicação, como é comumente usada em validações
- No contexto de validações é ruim para o usuário ter de ficar obtendo os erros um a um. É preferível obter uma lista completa de erros logo na primeira tentativa (como ocorre no uso de Notification Pattern)
- Você deveria se perguntar se ao remover todos os try/catch do código a sua aplicação continuaria rodando como esperado (no "fluxo principal" / "caso ideal"). Caso a resposta seja que não, provavelmente você está usando Exceptions do jeito errado.
- Existem contextos discutíveis, como validar a obtenção de um arquivo, que pode:
- Ser um arquivo bem conhecido e parte da base do Linux e que é esperado que sempre existirá, e a falha na obtenção pode ser uma exceção;
- Ser um arquivo informado pelo usuário, muito passível de erros, e a falha na obtenção deveria ser uma lógica de notificação, e não tratada por exceção.
- Um outro caso discutível é o uso de uma segunda validação de dados (que já foram validadas previamente no fluxo do código) mas que podem ser tratadas por exceção apenas como uma forma de garantir que um erro de programação não passou por engano (o esperado é realmente que esta exceção nunca seja atingida)
Implementar Notification Pattern:
- Uma simples lista de Strings de erros seria suficiente. Mas terceirizar uma classe para as notificações deixa a lógica mais legível
- Esta refatoração começa com a separação de 2 métodos:
-
validation()
devolve o objeto de Notification "populado" com os erros; -
check()
usa ovalidation()
para o obter o objeto de Notification e no caso dohasErrors()
ser true lança a exceção exatamente como antes. - Desta forma a estrutura original se mantem no
check()
e você pode ir refatorando os usos desta validação para passar a trabalhar diretamente com o objeto Notification devolvido pelo novo comportamentovalidation()
-
void check() {
Notification val = validation()
if (val.hasErrors())
throw new IllegalArgumentException(val.errorMessage())
}
Notification validation() {
Notification note = new Notification()
if (/*validacao*/) note.addError("mensagem de erro");
return note
}
- É importante lembrar que numa refatoração se deve garantir que o comportamento original seja mantido no código final, portanto:
- Nesta refatoração em específico se deve observar se o atual código que recebe a atual mensagem de exceção se preocupa diretamente com o conteúdo da mensagem. A intenção é decidir se no
check()
a exceção será lançada comerros.get(0)
ouerros.join(', ')
no retorno deval.errorMessage()
- Nesta refatoração em específico se deve observar se o atual código que recebe a atual mensagem de exceção se preocupa diretamente com o conteúdo da mensagem. A intenção é decidir se no
Sugestões adicionais genéricas de refatoração:
- Numa refatoração sempre use "baby steps" para preservar comportamento, evitando futuro debug para corrigir os comportamentos novos/desconhecidos/indesejados
- Ao refatorar um método de validação comece de baixo pra cima, isto te ajuda a garantir que você não irá se perder em alguns casos, como null por exemplo
- Padrão de refatoração - Extract Method
- Padrão de refatoração - Replace Nested Conditional with Guard Clauses
Posted by Bruno Vieira to ZeroGlosa (2016-03-22 18:00)