这些消息是给其他开发人员的
这些消息应由开发人员阅读,以帮助他们调试应用程序。这可以采取两种形式:
主动调试。您实际上是在编写代码并试图弄清楚发生什么时运行调试器。在这种情况下,有用的异常将通过使您易于理解问题所在或最终提出解决方法来指导您(尽管这是可选的)。
被动调试。该代码在生产中运行并且失败。记录了异常,但是您仅收到消息和堆栈跟踪。在这种情况下,有用的异常消息将帮助您快速定位错误。
由于这些消息经常被记录下来,因此这也意味着您不应在其中包括敏感信息(例如私钥或密码,即使对调试应用程序很有用)。
例如,IOSecurityException
写入文件时引发的异常类型不是很明确地说明该问题:是否是因为我们没有访问文件的权限?还是我们可以阅读但不能写?还是文件不存在,而我们没有在那里创建文件的权限?或者它可能已被锁定(希望这种情况下异常的类型会有所不同,但实际上,有时类型可能是隐秘的)。还是代码访问安全性阻止我们执行任何I / O操作?
代替:
IOSecurityException:已找到文件,但读取其内容的权限被拒绝。
更明确。在这里,我们立即知道目录权限设置正确(否则,我们将无法知道该文件存在),但是文件级别的权限存在问题。
这也意味着,如果您无法提供异常类型以外的任何其他信息,则可以将消息保留为空。 DivisionByZeroException
这是一个消息多余的好例子。另一方面,大多数语言都允许您在不指定异常消息的情况下引发异常的事实是出于不同的原因:要么是因为默认消息已经可用,要么是因为如果需要,它将在以后生成(并且生成此消息)。消息被包含在异常类型中,这很有意义,“ OOPly”。
请注意,出于技术(通常是性能)方面的原因,某些消息最终变得比应有的含义更加神秘。.NET的NullReferenceException
:
你调用的对象是空的。
是一条无用的信息的绝佳示例。一个有用的消息是:
product
在中调用时,对象引用未设置为对象的实例product.Price
。
这些消息不是给最终用户的!
最终用户不应看到异常消息。决不。尽管某些开发人员最终将这些消息显示给用户,但这导致糟糕的用户体验和沮丧感。消息如:
你调用的对象是空的。
对最终用户而言绝对没有任何意义,应不惜一切代价避免。
最坏的情况是使用全局try / catch,将异常抛出给用户并退出应用程序。一个关心用户的应用程序:
首先处理异常。大多数情况下都可以处理而不会打扰用户。网络已关闭?为什么不等几秒钟再试一次?
防止用户将应用程序引向特殊情况。如果您要求用户输入两个数字并将第一个数字除以第二个数字,那么为什么要让用户在第二种情况下输入零,以便在几秒钟后归咎于他?将文本框突出显示为红色(一个有用的工具提示,告诉数字应该不为零),然后禁用验证按钮,直到该字段保持红色该怎么办?
邀请用户以没有错误的形式执行操作。没有足够的权限访问文件?为什么不要求用户授予管理权限或选择其他文件?
如果没有其他效果,则显示一个有用的,友好的错误,该错误专门用于减少用户的挫败感,帮助用户了解出了什么问题并最终解决了该问题,并且还帮助他防止了以后的错误(适用时)。
在您的问题中,您建议有两个例外消息:一种是针对开发人员的技术性消息,另一种是针对最终用户的消息。尽管这在某些较小的情况下是一个有效的建议,但是大多数例外是在不可能为用户产生有意义的消息的级别上产生的。以DivisionByZeroException
想象,我们无法阻止发生的异常,并不能处理它自己。发生划分时,框架(因为是框架,而不是引发异常的业务代码)知道对用户有帮助的消息是什么?绝对不:
被零除。[好]
相反,可以让它引发异常,然后在我们了解业务上下文的更高级别捕获它,并可以采取相应措施以实际帮助最终用户,从而显示出类似以下内容:
字段D13的值不能与字段E6中的值相同,因为这些值的减法用作除数。[好]
或许:
ATP服务报告的值与本地数据不一致。这可能是由于本地数据不同步引起的。您要同步运输信息并重试吗?[是] [否]
这些消息不用于解析
异常消息也不希望被解析或以编程方式使用。如果您认为调用方可能需要其他信息,请将其与消息一起包含在异常中。这很重要,因为消息如有更改,恕不另行通知。类型是接口的一部分,但消息不是:永远不要依赖它进行异常处理。
想象一下异常消息:
等待500毫秒后,连接到缓存服务器超时。要么增加超时时间,要么检查性能监视以发现服务器性能下降。缓存服务器的平均等待时间为6毫秒。最后一个月4毫秒 最后一周和377毫秒。最后一个小时。
您要提取“ 500”,“ 6”,“ 4”和“ 377”值。考虑一下您将用于进行解析的方法,然后继续阅读。
你有主意吗?大。
现在,原始开发人员发现了一个错字:
Connecting to the caching sever timed out after waiting [...]
应该:
↓
Connecting to the caching server timed out after waiting [...]
此外,开发人员认为月/周/一个小时不是特别重要,因此他还进行了其他更改:
缓存服务器的平均等待时间为6毫秒。最后一个月5毫秒。在过去的24小时和377毫秒中。最后一个小时。
您的解析会怎样?
除了解析外,您还可以使用异常属性(可以在序列化数据后立即包含异常):
{
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 },
]
}
}
现在使用这些数据有多容易?
有时(例如在.NET中),消息甚至可以翻译成用户的语言(恕我直言,翻译这些消息是绝对错误的,因为任何开发人员都希望能够用英语阅读)。解析此类消息几乎是不可能的。