Essas mensagens são para outros desenvolvedores
Espera- se que essas mensagens sejam lidas pelos desenvolvedores para ajudá-los a depurar o aplicativo. Isso pode assumir duas formas:
Depuração ativa. Na verdade, você está executando um depurador enquanto escreve o código e tenta descobrir o que acontece. Nesse contexto, uma exceção útil o guiará, facilitando a compreensão do que está acontecendo de errado ou, eventualmente, sugerindo uma solução alternativa (embora isso seja opcional).
Depuração passiva. O código é executado na produção e falha. A exceção é registrada, mas você recebe apenas a mensagem e o rastreamento de pilha. Nesse contexto, uma útil mensagem de exceção o ajudará a localizar rapidamente o bug.
Como essas mensagens geralmente são registradas, isso também significa que você não deve incluir informações confidenciais (como chaves privadas ou senhas, mesmo que possam ser úteis para depurar o aplicativo).
Por exemplo, o IOSecurityException
tipo de exceção lançada ao gravar um arquivo não é muito explícito sobre o problema: é porque não temos permissões para acessar um arquivo? Ou talvez possamos ler, mas não escrever? Ou talvez o arquivo não exista e não tenhamos permissão para criar arquivos lá? Ou talvez esteja bloqueado (espero que o tipo de exceção seja diferente nesse caso, mas, na prática, os tipos às vezes podem ser enigmáticos). Ou talvez o Code Access Security nos impeça de realizar qualquer operação de E / S?
Em vez de:
IOSecurityException: o arquivo foi encontrado, mas a permissão para ler seu conteúdo foi negada.
é muito mais explícito. Aqui, sabemos imediatamente que as permissões no diretório estão definidas corretamente (caso contrário, não poderemos saber que o arquivo existe), mas as permissões no nível do arquivo são problemáticas.
Isso também significa que, se você não puder fornecer nenhuma informação adicional que ainda não esteja no tipo da exceção, poderá manter a mensagem vazia. DivisionByZeroException
é um bom exemplo em que a mensagem é redundante. Por outro lado, o fato de a maioria dos idiomas permitir que você lance uma exceção sem especificar sua mensagem é feito por um motivo diferente: porque a mensagem padrão já está disponível ou porque será gerada mais tarde, se necessário (e a geração dessa A mensagem está entre o tipo de exceção, que faz todo o sentido, "OOPly" falando).
Observe que, por razões técnicas (geralmente de desempenho), algumas mensagens acabam sendo muito mais enigmáticas do que deveriam. .NET NullReferenceException
:
Referência de objeto não definida para uma instância de um objeto.
é um excelente exemplo de mensagem que não é útil. Uma mensagem útil seria:
Referência de objeto product
não definida para uma instância de um objeto quando chamada product.Price
.
Essas mensagens não são para usuários finais!
Os usuários finais não devem ver mensagens de exceção. Nunca. Embora alguns desenvolvedores acabem mostrando essas mensagens para os usuários, isso leva a uma má experiência e frustração do usuário. Mensagens como:
Referência de objeto não definida para uma instância de um objeto.
não significa absolutamente nada para o usuário final e deve ser evitado a todo custo.
O pior cenário é ter uma tentativa / captura global que lança a exceção para o usuário e sai do aplicativo. Um aplicativo que se preocupa com seus usuários:
Lida com exceções em primeiro lugar. A maioria deles pode ser manuseada sem incomodar o usuário. A rede está inoperante? Por que não esperar alguns segundos e tentar novamente?
Impede que um usuário leve o aplicativo a um caso excepcional. Se você pedir ao usuário para digitar dois números e dividir o primeiro pelo segundo, por que você deixaria o usuário digitar zero no segundo caso para culpá- lo alguns segundos depois? Que tal destacar a caixa de texto em vermelho (com uma dica útil de ferramenta informando que o número deve ser diferente de zero) e desativar o botão de validação até que o campo permaneça vermelho?
Convida um usuário a executar uma ação em um formulário que não seja um erro. Não há permissões suficientes para acessar um arquivo? Por que não pedir ao usuário para conceder permissões administrativas ou escolher um arquivo diferente?
Se nada mais funcionar, mostra um erro útil e amigável, escrito especificamente para reduzir a frustração do usuário, ajudar o usuário a entender o que deu errado e, eventualmente, resolver o problema, além de ajudá-lo a evitar o erro no futuro (quando aplicável).
Na sua pergunta, você sugeriu duas mensagens em uma exceção: uma técnica para desenvolvedores e outra para usuários finais. Embora essa seja uma sugestão válida em alguns casos menores, a maioria das exceções é produzida em um nível em que é impossível produzir uma mensagem significativa para os usuários. Pegue DivisionByZeroException
e imagine que não poderíamos impedir a exceção de acontecer e não podemos lidar com isso sozinhos. Quando a divisão ocorre, a estrutura (já que é a estrutura, e não o código comercial, que gera a exceção) sabe qual será uma mensagem útil para um usuário? Absolutamente não:
A divisão por zero ocorreu. [ESTÁ BEM]
Em vez disso, pode-se deixar lançar a exceção e depois capturá-la em um nível superior, onde conhecíamos o contexto comercial e poderíamos agir de acordo para realmente ajudar o usuário final, mostrando assim algo como:
O campo D13 não pode ter o valor idêntico ao do campo E6, porque a subtração desses valores é usada como um divisor. [ESTÁ BEM]
ou talvez:
Os valores relatados pelo serviço ATP são inconsistentes com os dados locais. Isso pode ser causado pela falta de sincronização dos dados locais. Deseja sincronizar as informações de remessa e tentar novamente? [Sim não]
Essas mensagens não são para análise
As mensagens de exceção também não devem ser analisadas ou usadas programaticamente. Se você acha que informações adicionais podem ser necessárias pelo responsável pela chamada, inclua-as na exceção lado a lado com a mensagem. Isso é importante, porque a mensagem está sujeita a alterações sem aviso prévio. O tipo faz parte da interface, mas a mensagem não é: nunca dependa dela para tratamento de exceções.
Imagine a mensagem de exceção:
O tempo limite da conexão com o servidor de cache atingiu o tempo limite depois de esperar 500 ms. Aumente o tempo limite ou verifique o monitoramento de desempenho para identificar uma queda no desempenho do servidor. O tempo médio de espera para o servidor de armazenamento em cache foi de 6 ms. no último mês, 4 ms. para a última semana e 377 ms. pela última hora.
Você deseja extrair os valores "500", "6", "4" e "377". Pense um pouco na abordagem que você usará para fazer a análise e só então continue lendo.
Você tem a ideia? Ótimo.
Agora, o desenvolvedor original descobriu um erro de digitação:
Connecting to the caching sever timed out after waiting [...]
deveria estar:
↓
Connecting to the caching server timed out after waiting [...]
Além disso, o desenvolvedor considera que o mês / semana / uma hora não são particularmente relevantes, portanto, ele também faz uma alteração adicional:
O tempo médio de espera para o servidor de armazenamento em cache foi de 6 ms. no último mês, 5 ms. nas últimas 24 horas e 377 ms. pela última hora.
O que acontece com a sua análise?
Em vez de analisar, você pode usar propriedades de exceção (que podem conter o que se deseja, assim que os dados podem ser serializados):
{
message: "Connecting to the caching [...]",
properties: {
"timeout": 500,
"statistics": [
{ "timespan": 1, "unit": "month", "average-timeout": 6 },
{ "timespan": 7, "unit": "day", "average-timeout": 4 },
{ "timespan": 1, "unit": "hour", "average-timeout": 377 },
]
}
}
Quão fácil é usar esses dados agora?
Às vezes (como no .NET), a mensagem pode até ser traduzida para o idioma do usuário (IMHO, a tradução dessas mensagens é absolutamente errada, pois é esperado que qualquer desenvolvedor possa ler em inglês). A análise dessas mensagens é quase impossível.