删除空检查和默认值

“通向善良道路通往地狱。”

这个古老的谚语在软件开发中和生活中一样真实。

以以下代码为例:

public List<Product> getProducts(String country) {
if (country == null) {
country = „at“;
}
List<Product> products =
database.getProductsForCountries(country);
if (products == null) {
return Collection.emptyList();
}
else {
return products;
}
}

不要把它扫到地毯下

在我们的示例中,该函数返回给定国家/地区的产品列表。 如果国家/地区的值为空,则只需设置默认值(在本例中为奥地利),然后继续进行即可,就像一切都很好一样。

我们还考虑了基础数据库函数可能返回null的可能性。 同样,我们覆盖原始结果,并返回一个空列表而不是null。

这里有什么问题? 这些步骤使我们的应用程序尽可能地避免错误。 使用我们的代码是一种很好的态度。

但是,在这种情况下,我们的好意会导致不幸的结果。 这种模式隐藏了潜在的错误。 更糟糕的是,这些错误变得很难发现。

用户向我们发送错误报告时

想象一下,调用函数使用GeoIP解析当前用户所在的国家/地区。 我们永远不会知道它是否曾经发生故障并返回null。 该应用程序的行为就像一切正常。 我们怎么知道我们所有的客户现在都是奥地利人? 如果我们的大多数客户是奥地利人,则该错误将更容易被忽略。

单元测试对我们没有帮助,因为这是一个概念性错误,是由于不考虑我们的编码操作的后果而导致的。

只有当我们的客户抱怨说,他们不希望支付欧元,因为他们使用美元,或当我们的船舶代理指出,洛杉矶是不是在奥地利,只有到那时我们意识到,事情并没有工作,我们的思维方式他们是。

不断增长的代码库

毫不奇怪,我们函数中的多个参数或对其他类的依赖项将需要空检查。 我们的示例只有一个带有“数据库”对象的示例,但它引起了许多问题。

每个其他的空检查都会引入一个需要其自己的单元测试的新分支。 每个分支都使读者分心于代码的原始意图。

大量专门用于空检查的单元测试不必要地增加了我们的代码库。 请记住,我们必须维护这些检查。 如果我们修改代码,则必须调整所有这些附加测试。

所有这些额外的工作侵蚀了有效TDD所需的纪律。

对待异常是什么:异常

空值会立即中断执行。 这是一个很好的行为。 让它发生。 当系统状态出现问题时,我们希望在我们的开发机上看到一条大的NullPointerException错误消息。

不要继续进行,不要发生任何事情。

如果永不抛出NullPointerException怎么办? 那么,这意味着我们的代码从不访问该对象,并且我们可能根本不需要它。 我不会将该问题归为bug,而是潜在的死代码。

但是它可以为空!

容易想到null值自动是错误的。 请记住,空值可以是完全有效的结果。 例如,用户可以请求尚不存在的购物篮。 在这种情况下,我们可以使用Java的Optional-或声明Exception或您选择的语言所提供的任何内容。

不要期望呼叫者或生产者纠正您的“错误”。 向他们展示返回null是一种必须注意的有效方案。

我们应该删除所有验证吗? 你是认真的吗?

一点都不! 相反。 我们应该进行有效性检查,例如复杂性标准,使用现有电子邮件进行的新注册等等。

我的意思是,您不应覆盖null的默认值。 我们不只是想要验证。 我们希望异常抛出的次数过多而不是很少。

如果为null,则无需手动进行验证。 运行时为我们做到了。

API或库供应商的不同游戏

当我们提供API或库时,情况有所不同。 简而言之,我们的最终用户是技术人员或机器。

在这种情况下,我们要返回详细的错误描述。 最终用户需要确切地了解请求消息中的错误才能解决。

包起来

看看当我们将初始代码示例简化为以下内容时会发生什么:

public List<Product> getProducts(String country) { 
return database.getProductsForCountries(country);
}

如果国家/地区为空,则您的数据库库将引发错误消息。 如果数据库返回null,则您的调用程序函数将引发NullPointerException。 应用程序以任何一种方式中断。

我们不要过度使用有效性检查。 这将使我们的代码库非常简短。

我们的运行时应该抛出NullPointerExceptionUncaughtTypeError或用我们的语言调用的任何东西。 切勿让StackTrace突破。 向最终用户显示一个不错的通用错误消息。

同时,请确保您有一个集中的错误记录应用程序(例如Sentry)来收集所有可用信息,如StackTrace,上下文信息,已执行的SQL语句等。

听起来有些违反直觉,让我们的运行时抛出空错误,使我们作为开发人员能够快速安全地修复根本原因。

我的最终建议:不要低估从代码删除中获得的生产率提高。

最初于 2017年12月9日 发布在 www.rainerhahnekamp.com 上。

From: https://hackernoon.com/remove-null-checks-and-default-values-a0dda7ba872b