警告!C ++程序员带着各种不同的想法进入这里,试图回答一个肯定与另一种语言有关的问题,应该如何进行异常处理!
有了这个想法:
例如,假设我们有获取资源,该资源执行HTTP请求并返回检索到的数据。而不是像ServiceTemporaryUnavailable或RateLimitExceededed这样的错误,我们只会引发RetryableError,建议使用者它应该仅重试该请求,而不关心特定的失败。
...我想建议的一件事是,您可能会把报告错误的担忧与采取的应对措施相提并论,以可能降低代码通用性或要求大量“转换点”作为例外的方式。
例如,如果我对涉及加载文件的事务进行建模,则可能由于多种原因而失败。加载文件可能涉及加载用户计算机上不存在的插件。也许文件只是损坏了,我们在解析文件时遇到了错误。
不管发生什么情况,我们都可以说要采取的行动是向用户报告发生的情况,并提示用户要执行的操作(“重试,加载另一个文件,取消”)。
投掷者VS捕手
无论在这种情况下我们遇到哪种错误,都应遵循该行动方针。它没有嵌入到解析错误的一般思想中,也没有嵌入未能加载插件的一般思想中。它嵌入了在加载文件的精确上下文(加载文件和失败的组合)期间遇到此类错误的想法。因此,通常来说,粗略地说,我将其视为catcher's
确定响应抛出的异常(例如,向用户提供选项的提示)而采取的措施的责任,而不是thrower's
。
换句话说,throw
例外的站点通常缺少这种上下文信息,尤其是当抛出的功能通常适用时。即使在完全分散的环境中,当他们拥有此信息时,您也最终通过将其嵌入到throw
站点中而在恢复行为方面陷入困境。catch
通常是那些拥有最多信息的站点,这些站点可用来确定某项行动方案,并为您提供一个中心位置来修改该行动方案是否应针对给定交易进行更改。
当您开始尝试引发异常时,不再报告错误所在,而是尝试确定要做什么,这可能会降低代码的通用性和灵活性。解析错误不应总是导致这种提示,它会因引发此类异常(引发异常的事务)的上下文而异。
盲人投掷者
通常,异常处理的许多设计通常围绕盲目抛出的思想进行。它不知道异常将如何被捕获,或者在哪里。对于使用手动错误传播的更旧形式的错误恢复,也是如此。遇到错误的网站不包含用户的操作方针,它们仅嵌入最少的信息以报告遇到的错误类型。
责任倒置和麦田守望者
在更仔细地考虑这一点时,我试图想象这种代码库可能成为一种诱惑。我的想象力(可能是错误的)是,您的团队仍在扮演“消费者”的角色,并且也实现了大多数调用代码。也许您有很多不同的事务(很多try
块),所有事务都可能遇到相同的错误集,并且从设计角度来看,所有事务都应导致统一的恢复操作过程。
考虑到Lightness Races in Orbit's
良好答案的明智建议(我认为这实际上是来自于面向图书馆的高级思维方式),您可能仍然倾向于抛出“做什么”异常,而仅在事务恢复站点附近。
在这里可能会找到一个中间的,通用的交易处理站点,该站点实际上集中了“该做什么”的关注点,但仍在捕获的范围内。

仅当您可以设计所有这些外部事务都使用的某种通用函数(例如:输入另一个函数以调用的函数或具有可重写行为的抽象事务基类来建模此做复杂捕获的中间事务站点)时,这才适用)。
然而,这可能是负责响应各种可能的错误而集中用户的操作过程,并且仍然在捕获而不是抛出的范围内。简单的示例(Python式的伪代码,我丝毫也不是经验丰富的Python开发人员,因此可能会有更惯用的方式进行此操作):
def general_catcher(task):
try:
task()
except SomeError1:
# do some uniformly-designed recovery stuff here
except SomeError2:
# do some other uniformly-designed recovery stuff here
...
[希望有一个比general_catcher
] 更好的名字。在此示例中,您可以传入一个函数,该函数包含要执行的任务,但仍会因您感兴趣的所有异常类型而从通用/统一捕获行为中受益,并继续扩展或修改所有“做什么”部分您喜欢从这个中心位置出发,但仍处于catch
通常鼓励这样做的环境中。最重要的是,我们可以使用“该做什么”(保留“盲人投掷者”的概念)来防止投掷场所对自己的关注。
如果您发现这里的这些建议均无济于事,并且无论如何都会抛出“做什么”异常的强烈诱惑,主要是要意识到,这至少是非常反习惯的做法,并且有可能阻止普遍的思维方式。