返回null坏设计?

koe*_*oen 123 oop null return-value

我听到一些声音说从方法中检查返回的空值是糟糕的设计.我想听听一些原因.

伪代码:

variable x = object.method()
if (x is null) do something
Run Code Online (Sandbox Code Playgroud)

Ada*_*ski 198

不返回null的基本原理是您不必检查它,因此您的代码不需要根据返回值遵循不同的路径.您可能想要查看Null对象模式,它提供了有关此内容的更多信息.

例如,如果我在Java中定义一个返回Collection的方法,我通常更喜欢返回一个空集合(即Collections.emptyList())而不是null,因为这意味着我的客户端代码更清晰; 例如

Collection<? extends Item> c = getItems(); // Will never return null.

for (Item item : c) { // Will not enter the loop if c is empty.
  // Process item.
}
Run Code Online (Sandbox Code Playgroud)

......比以下更清洁:

Collection<? extends Item> c = getItems(); // Could potentially return null.

// Two possible code paths now so harder to test.
if (c != null) {
  for (Item item : c) {
    // Process item.
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我很高兴地同意,当它用作空容器(或字符串)的替代品时,返回null是疯了.但这不是常见的情况. (9认同)
  • 是的,比返回null更好,希望客户记得处理这个案子. (5认同)
  • 评论'//现在有两个可能的代码路径'不正确; 你有两种代码路径.但是,在第一个示例中使用null Collection对象时,"null"代码路径更短.您仍然有两条路径,但仍需要测试两条路径. (4认同)
  • 对我来说也为空对象模式 +1。此外,当我实际上想要返回 null 时,我一直将方法命名为 getCustomerOrNull() 以使其明确。我认为当读者不想看实现时,方法命名得很好。 (2认同)

Max*_*yak 67

这就是原因.

罗伯特·马丁的"清洁代码"中,他写道,当你可以改为返回空数组时,返回null是糟糕的设计.由于预期的结果是数组,为什么不呢?它将使您能够在没有任何额外条件的情况下迭代结果.如果它是一个整数,那么0就足够了,如果它是一个散列的空哈希.等等

前提是不强制调用代码来立即处理问题.调用代码可能不想关注它们.这也是为什么在许多情况下例外优于零.

  • +1用于引用叔叔的Bob书 (3认同)
  • 这是这个答案中提到的Null对象模式:http://stackoverflow.com/questions/1274792/is-returning-null-bad-design/1274822#1274822 (2认同)

Zer*_*ept 37

返回null的好用法:

  • 如果null是有效的函数结果,例如:FindFirstObjectThatNeedsProcessing()如果未找到则返回null,并且调用者应相应地进行检查.

不良用途:试图替换或隐藏异常情况,例如:

  • catch(...)并返回null
  • API依赖初始化失败
  • 磁盘空间不足
  • 输入参数无效(编程错误,输入必须由调用者清理)
  • 等等

在这些情况下,抛出异常更加充分,因为:

  • null返回值不提供有意义的错误信息
  • 直接调用者很可能无法处理错误情况
  • 无法保证调用者正在检查null结果

但是,异常不应用于处理正常的程序操作条件,例如:

  • 用户名/密码无效(或任何用户提供的输入)
  • 打破循环或作为非本地的getos

  • 输入无效登录/密码的用户不应被视为异常条件,这是正常程序操作的一部分.但是,认证系统未能响应(例如,活动目录)是一种例外情况. (10认同)
  • 但是,"无效的用户名"似乎是异常的一个很好的原因. (7认同)
  • 我要说它取决于:`boolean login(String,String)`似乎很好``AuthenticationContext createAuthContext(String,String)抛出AuthenticationException` (2认同)
  • 在您的示例中,如果没有需要处理的第一个对象,则有两种合理的方法来处理这种情况。第一个是空对象模式。创建一个代表该类不存在版本的最终子类。它具有合理的默认值,并在请求无意义的操作时抛出异常。另一种技术是选项。这是 Java8 和 Scala 中可用的功能。这些都表明了开发人员的意图。null 无法显示意图,因为它有太多可能的含义。** 显示意图** 是代码最重要的方面。 (2认同)

yeg*_*256 27

是的,在面向对象的世界中,返回NULL是一种糟糕的设计.简而言之,NULL使用会导致:

  • 临时错误处理(而不是异常)
  • 含糊不清的语义
  • 慢而不是快速失败
  • 计算机思维而不是对象思维
  • 可变和不完整的对象

查看此博客文章以获取详细说明:http://www.yegor256.com/2014/05/13/why-null-is-bad.html.更多内容在我的书中,优雅对象,第4.1节.

  • 我强烈同意.我甚至不喜欢看似"粉饰"延迟策略的空对象模式.您的方法要么总是成功,要么可能不成功.如果它应该总是成功,那么扔.如果它可能不成功,那么设计方法让消费者知道,例如`bool TryGetBlah(out blah)`或`FirstOrNull()`或`MatchOrFallback(T fallbackValue)`. (2认同)

Bra*_*don 23

谁说这是糟糕的设计?

检查空值是一种常见的做法,甚至是鼓励的,否则你将面临NullReferenceExceptions的风险.更好地处理错误优雅而不是在不需要时抛出异常.

  • @Preston:我非常不同意.没有空检查,当它崩溃时你会立即发现.被吞噬的异常可能会多年来传播神秘和微妙的错误...... (20认同)
  • +1.抛出可以在现场缓解的问题的异常的代码让我感到难过. (11认同)
  • 当编码员忘记检查空值然后获得神秘的空指针异常时,你有多难过?检查异常,编译器提醒用户他们没有处理错误条件,避免了类编码错误. (6认同)
  • 与没有空检查相比,更容易发现空捕获块的存在. (5认同)
  • @djna:我想这也很难过,但我发现那些"忘记"检查空值的编码器是那些在处理检查异常时经常会吞下它们的人. (3认同)
  • 我觉得你错过了这一点.当然,如果可以返回空值,则需要检查空值.问题是,如果你正在设计代码,你应该返回null,这会强制用户检查空值 - 考虑到替代方案,这可能是不必要的.例如,请参见Null对象模式. (2认同)

jco*_*lum 17

根据你到目前为止所说的,我认为没有足够的信息.

从CreateWidget()方法返回null似乎很糟糕.

从FindFooInBar()方法返回null似乎很好.

  • 类似于我的惯例:**单项查询 - **`Create ...`返回一个新实例,*或throws*; `Get ...`返回一个预期的现有实例,*或throws*; `GetOrCreate ...`返回一个现有实例,如果不存在则返回新实例,*或throws*; `Find ...`返回一个现有实例,如果它存在,*或`null`*.**对于集合查询 - **`Get ...`总是返回一个集合,如果没有找到匹配的项目则为空. (4认同)

Bah*_*ğan 12

它的发明者这是一个十亿美元的错误!


Gre*_*ech 10

这取决于您使用的语言.如果您使用的语言类似于C#,其中指示缺少值的惯用方法是返回null,那么如果您没有值,则返回null是一个很好的设计.或者,在诸如Haskell之类的语言中,在这种情况下习惯使用Maybe monad,然后返回null将是一个糟糕的设计(如果它甚至可能).

  • +1 提到可能是 monad。我发现诸如 C# 和 Java 之类的语言中 `null` 的定义经常被重载并在域中被赋予一些含义。如果您在语言规范中查找 `null` 关键字,它仅表示“无效指针”。这可能在任何问题领域都没有任何意义。 (2认同)

Joh*_*ell 6

我在这个领域有一个会议,对我很有帮助

对于单项查询:

  • Create...返回一个新实例,或者抛出
  • Get...返回预期的现有实例,或抛出
  • GetOrCreate...返回现有实例,如果不存在则返回新实例,或者抛出异常
  • Find...返回现有实例(如果存在),或者null

对于集合查询:

  • Get...始终返回一个集合,如果没有找到匹配的[1]项,则该集合为空

[1] 给定一些标准,显式或隐式,在函数名称中或作为参数给出。


Bor*_*ens 5

如果您阅读了所有答案,那么这个问题的答案就会变得清晰,取决于方法的种类.

首先,当异常发生时(IOproblem等),抛出逻辑异常.当确切的东西是特殊的可能是一个不同的主题..

每当一个方法预计可能没有结果时,有两个类别:

  • 如果可以返回中性值,请执行此操作.
    空的enumrables,字符串等是很好的例子
  • 如果不存在这样的中性值,则应返回null.
    如上所述,假设该方法可能没有结果,因此它不是例外,因此不应抛出异常.中性值是不可能的(例如:0不是特别中性的结果,具体取决于程序)

直到我们有一个官方的方式来表示函数可以或不能返回null,我尝试使用命名约定来表示.
就像你对预期失败的方法有Try Something()约定一样,当方法返回中性结果而不是null时,我经常将我的方法命名为Safe Something().

我还没有完全确定这个名字,但是无法想出更好的东西.所以我现在正在使用它.