假设我写了一个函数,它期望输入是a或B。输入可以是C,这在技术上是有效的,但如果程序的其余部分逻辑正常工作,则应该是不可能的。

我应该:

添加if-else子句来处理所有可能的输入,并在必要时抛出异常/返回null。

假设输入不能是C,而只能处理其余部分。

这两个变体中哪一个更好?第一种变体允许我们尽快捕捉错误,但会导致非常混乱和难以读取的代码。第二个变量导致代码更干净,但是如果程序的其余部分工作不正常,则错误可能会传播到远离其来源的地方。

这是为了解决程序的内部逻辑,而不是用户输入,显然我们应该能够处理所有可能的输入。

最佳回复

假设你用的语言是面向对象的,而不是功能性的。


运行时检查

难以阅读的代码?为什么?可以通过表示某些值无效来启动该方法。这会使阅读/理解变得更容易。

无论如何,验证是另一个关注点,你应该知道它从哪里开始,从哪里结束,重构你的代码,这样容易看到,从而提高可读性。如果验证输入的代码实际上非常复杂,以至于难以阅读,这就提示您应该将验证提取到另一个方法。

我知道有些情况下,如何做到这一点而不牺牲效率是不明显的。例如,验证一个列表可能意味着遍历它。然后再重复一遍以实际使用它。。。好吧,如果您有一个解决方案,比如Linq或类似的解决方案,那么您应该能够编写独立于使用列表的验证代码,同时只对其进行一次迭代,这要感谢Linq被惰性地评估。

您可能还希望避免代码内部的重复验证。。。您可以使用条件编译、调试断言、代码契约或类似的解决方案,只在开发和测试环境中进行额外的验证。


编译时检查

如果您有一个好的代码契约解决方案,它将在生成时验证。

说到验证代码的工具。。。你能得到或写一个吗?我正在考虑lint或类似的工具,它可以检查代码的前提条件,并添加到构建集成中。我知道这样的工具并不总是合算的,但是如果你的情况足够具体和重要,那么这样的工具是有价值的。

但是,最好将验证表示为类型系统的一部分,这样在编译时就可以检查它们,而不需要额外的工具。

例如,你可以 T ,和类型 ValidatedT ,只能使用 T . 那么你的内部代码将只接受 ValidatedT (当然,使用一个更好的名称),确保验证已经发生,并且不管有多少方法 ValidatedT 值被传递,验证只发生一次。这也会提高可读性,因为您可以清楚地看到 T 你在哪里 ValidatedT . 不仅如此,你还可以 ValidatedT 对于不同的情况。。。

我是说,如果我说类型 string ,是什么?用户名?产品说明?什么?但是,如果我说类型 Username ,然后您知道它在系统中是什么,并且您知道它是一个有效值。

哦,顺便说一下,如果使用类型别名,您仍然可以获得更易于阅读的代码的好处,尽管没有编译时检查。