Object Validation: to Defer or Not?

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

我之前说过构造函数必须是无代码的,只做属性初始化而已。从那时起,最常被问到的问题是:那参数的验证呢?如果它们是“破损”的,创建一个“无效”状态的对象还有什么意义呢?这样的对象将在以后的某个意想不到的时刻失败。难道不是在实例化的那一刻就抛出异常更好吗?所谓“快速失败”,我认为应该是这样的。

让我们从这段Ruby代码开始:

我们可以使用它从文件中读取用户列表。

有很多滥用这个类的方法:

  • 传递其他东西,而不是 String

  • 传递一个不存在的文件;

  • 传递一个目录而不是一个文件。

你看到我们可以犯的这四个错误之间的区别了吗?让我们看看我们的班级如何保护自己免受每个错误的影响:

第一个和第二个潜在错误在构造函数中被过滤掉了,而另外两个在方法中被过滤掉了。为什么我要这样做呢?为什么不把它们都放在构造函数里?

因为前两个会影响对象的状态,而另外两个会影响其运行时行为。你还记得,对象是一组封装的其他对象的代表,称为属性。Users类的对象不能代表nil或者一个数字。它只能代表一个类型为String的文件名。另一方面,文件的内容以及它是否真的是一个文件,不会使状态无效。它只会给行为带来麻烦。

尽管这个差异看起来微妙,但很明显。与封装对象的交互有两个阶段:“连接”和“交流”。

首先,我们封装了file并且希望确保它确实是一个文件。我们还没有开始与它交流,我们不希望它为我们工作,我们只是想确保它真的是我们将来能够与之交流的对象。如果它是nil或者一个float,我们将来肯定会遇到问题。这就是为什么我们在构造函数中抛出异常。

然后第二阶段是交流,在这个阶段我们将控制权委托给对象,并期望它能正确地行为。在这个阶段,我们可能还有其他验证过程,以确保我们的交互能够顺利进行。重要的是要提到,这些验证是非常情境化的。我们可能多次调用names(),每次与磁盘上的文件都有不同的情况。一开始它可能不存在,而几秒钟后它就会准备好并可供读取。

理想情况下,编程语言应该提供用于第一类型验证的工具,例如强类型。例如,在Java中,我们不需要检查file的类型,编译器会更早地捕获到该错误。在Kotlin中,由于他们的Null Safety特性,我们可以摆脱NULL检查。Ruby比这些语言弱大一些,这就是为什么我们必须手动验证的原因。

因此,总结起来,在构造函数中进行验证不是一个坏主意,前提是验证不涉及对象,而只是确认它们是否足够好以供以后使用。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-27 at 14:01

sixnines availability badge   GitHub stars