但是崩溃客户软件仍然不是一件好事
这当然是一件好事。
您想要使系统处于未定义状态的任何东西都可以停止系统,因为未定义的系统会执行令人讨厌的事情,例如数据损坏,格式化硬盘以及发送威胁总统的电子邮件。如果您无法恢复并使系统回到定义的状态,那么崩溃是要做的事情。这就是为什么我们构建的系统崩溃而不是悄悄地将其自身拆散。现在确定,我们都希望有一个不会崩溃的稳定系统,但是只有当系统保持在已定义的可预测安全状态时,我们才真正想要它。
我听说作为控制流的异常被认为是严重的反模式
这是绝对正确的,但常常被误解。当他们发明异常系统时,他们担心会破坏结构化编程。结构化编程就是为什么我们有for
,while
,until
,break
,和continue
当所有我们需要的,做的所有这一切,是goto
。
Dijkstra告诉我们,非正式地使用goto(也就是说,随意跳动)会使阅读代码成为噩梦。当他们给我们例外系统时,他们担心自己在重塑goto。因此,他们告诉我们不要“将其用于流量控制”,希望我们能够理解。不幸的是,我们许多人没有。
奇怪的是,我们不像过去使用goto那样经常使用异常来创建意大利面条代码。该建议本身似乎造成了更多麻烦。
从根本上来说,例外是关于拒绝一个假设。当您要求保存文件时,假定该文件可以并且将被保存。当您无法使用该名称时,您可能会收到一个例外,该名称可能是名称不合法,HD已满,或者是因为老鼠通过您的数据线咬了。您可以以不同的方式处理所有这些错误,可以以相同的方式处理它们,也可以让它们停止系统。您的代码中有一条快乐的路,您的假设必须成立。一种或另一种例外会让您走开那条快乐的路。严格来说,是的,这是一种“流控制”,但这并不是他们警告您的。他们说的是这样的废话:
“例外应该是例外”。这种重言巧语是天生的,因为异常系统设计人员需要时间来构建堆栈跟踪。与跳动相比,这很慢。它占用了CPU时间。但是,如果您要在启动下一个系统之前记录并停止系统,或者至少停止当前时间密集的处理,那么您就有时间消磨时间。如果人们开始使用“用于流控制”的例外,那么关于时间的所有假设都将落空。因此,出于性能考虑,确实给了我们“例外应该是例外的”。
比这更重要的是不要使我们感到困惑。您在上面的代码中发现无限循环花了多长时间?
不要返回错误代码。
当您使用的代码库通常不使用错误代码时,...是很好的建议。为什么?因为没有人会记得保存返回值并检查错误代码。当您使用C语言时,这仍然是一个很好的约定。
OneOf
您正在使用另一种约定。只要您设置约定而不是简单地与另一个约定打交道,那很好。在同一代码库中有两个错误约定会令人困惑。如果您以某种方式摆脱了使用其他约定的所有代码,请继续。
我本人喜欢这个约定。我在这里找到的最好的解释之一*:
但是,尽管我很喜欢,但我仍然不会将其与其他约定混合使用。选择一个并坚持下去。1个
1:我的意思是不要让我同时考虑多个公约。
随后的想法:
从这次讨论中,我目前要讲的内容如下:
- 如果您希望直接调用方在大多数时间捕获并处理异常并继续其工作(也许通过另一条路径),则它可能应该是返回类型的一部分。Optional或OneOf在这里可能有用。
- 如果您希望直接调用方在大多数情况下不会捕获异常,请抛出异常,以免手动将异常传递到堆栈中。
- 如果您不确定直接调用者将要做什么,则可以同时提供两者,例如Parse和TryParse。
真的不是那么简单。您需要了解的基本知识之一是什么是零。
五月还剩几天?0(因为不是5月。已经是6月)。
例外是拒绝假设的一种方法,但不是唯一的方法。如果您使用异常来拒绝该假设,那么您将走开心的路。但是,如果您选择了值来沿幸福的道路发送信号,表明事情并没有想象中的那么简单,那么您可以一直坚持下去,只要它可以处理这些值即可。有时,0已经用来表示某些东西,因此您必须找到另一个值以将拒绝假设的想法映射到该值。您可能会从在好的旧代数中使用它的想法认识到这一点。Monad可以帮助您解决问题,但不一定总是Monad。
例如2:
IList<int> ParseAllTheInts(String s) { ... }
您能想到有什么好的理由必须对它进行设计,以使其有意抛出任何东西吗?猜猜无法解析int会得到什么?我什至不需要告诉你。
这是一个好名字的标志。抱歉,TryParse不是我的好名字。
当答案可能同时不止是一件事时,我们通常避免抛出什么也不会抛出异常,但是由于某种原因,如果答案是一件事还是一无所获,我们就会坚持要求它给我们一件事或抛出:
IList<Point> Intersection(Line a, Line b) { ... }
平行线真的需要在这里引起异常吗?如果此列表永远不会包含一个以上的点,真的那么糟糕吗?
也许从语义上讲您不能接受。如果是这样,那太可惜了。但是,也许Monads没有像List
这样的任意大小,会让您感觉更好。
Maybe<Point> Intersection(Line a, Line b) { ... }
Monads是一些特殊用途的集合,旨在以特定方式使用,而无需测试它们。我们应该找到处理它们的方法,无论它们包含什么。这样,幸福的道路保持简单。如果您打开并测试每一个Monad,那么您使用的是错误的。
我知道,这很奇怪。但这是一个新工具(对我们而言)。所以给它一些时间。当您停止在螺钉上使用锤子时,锤子会更有意义。
如果您可以纵容我,我想发表评论:
为什么没有答案能说明Emoner monad不是错误代码,也不是OneOf?它们本质上是不同的,因此问题似乎是基于一种误解。(尽管以修改后的形式仍然是一个有效的问题。)– Konrad Rudolph 6月18日在18:08时
这是绝对正确的。Monad比异常,标志或错误代码更接近集合。如果明智地使用它们,它们确实可以为此类物品制造精美的容器。
Result
;-)