返回null坏设计?

时间:2009-08-13 21:47:02

标签: oop null return-value

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

伪代码:

variable x = object.method()
if (x is null) do something

24 个答案:

答案 0 :(得分:193)

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

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

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

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

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

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.
  }
}

答案 1 :(得分:66)

这就是原因。

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

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

答案 2 :(得分:36)

返回null的好用法:

  • 如果null是有效的功能结果,例如:FindFirstObjectThatNeedsProcessing()如果找不到则返回null,并且调用者应该相应地进行检查。

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

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

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

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

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

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

答案 3 :(得分:26)

是的,在面向对象的世界中,返回NULL是一个糟糕的设计。简而言之,NULL用法导致:

  • 临时错误处理(而非例外)
  • 模棱两可的语义
  • 慢而不是快速失败
  • 计算机思维而非对象思维
  • 可变和不完整的对象

查看此博客文章,了解详细说明:http://www.yegor256.com/2014/05/13/why-null-is-bad.html。更多内容见我的书Elegant Objects,第4.1节。

答案 4 :(得分:23)

谁说这是糟糕的设计?

检查空值是一种常见的做法,即使是鼓励,否则您将面临NullReferenceExceptions的风险。最好处理错误,而不是在不需要时抛出异常。

答案 5 :(得分:17)

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

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

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

答案 6 :(得分:12)

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

答案 7 :(得分:10)

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

答案 8 :(得分:5)

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

首先,当异常发生时(IOproblem等),抛出逻辑异常。当确切的事情出现异常时,可能是针对不同主题的事情......

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

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

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

我还没有完全确定这个名字,但是无法提出更好的结果。所以我现在正在使用它。

答案 9 :(得分:3)

这不一定是一个糟糕的设计 - 因为有很多设计决定,它取决于它。

如果方法的结果在正常使用中不会有好结果,那么返回null就可以了:

object x = GetObjectFromCache();   // return null if it's not in the cache

如果确实应该总是存在非null结果,那么抛出异常可能会更好:

try {
   Controller c = GetController();    // the controller object is central to 
                                      //   the application. If we don't get one, 
                                      //   we're fubar

   // it's likely that it's OK to not have the try/catch since you won't 
   // be able to really handle the problem here
}
catch /* ... */ {
}

答案 10 :(得分:3)

异常适用于例外情况。

如果您的函数旨在查找与给定对象关联的属性,并且该对象确实没有此类属性,则返回null可能是合适的。如果该对象不存在,则抛出异常可能更合适。如果该函数是为了返回一个属性列表,并且没有返回属性,那么返回一个空列表是有意义的 - 你将返回所有零属性。

答案 11 :(得分:3)

我在这方面有一个很适合我的会议

对于单项查询:

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

对于集合查询:

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

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

答案 12 :(得分:3)

如果这样做有意义,可以返回null:

public String getEmployeeName(int id){ ..}

在这种情况下,如果id与现有实体不对应,则返回null是有意义的,因为它允许您区分未找到匹配的情况与合法错误。

人们可能认为这很糟糕,因为它可以被滥用作为表示错误情况的“特殊”返回值,这不是很好,有点像从函数返回错误代码但由于用户必须检查而令人困惑返回null,而不是捕获适当的异常,例如

public Integer getId(...){
   try{ ... ; return id; }
   catch(Exception e){ return null;}
}

答案 13 :(得分:2)

您认为返回null有哪些替代方法?

我看到两个案例:

  • findAnItem(id)。如果找不到该项,该怎么办

在这种情况下,我们可以:返回Null或抛出(已检查)异常(或者创建一个项目并将其返回)

  • listItemsMatching(criteria)如果找不到任何内容,应返回什么内容?

在这种情况下,我们可以返回Null,返回一个空列表或抛出异常。

我认为返回null可能不如替代品好,因为它要求客户端记住检查null,程序员忘记和编码

x = find();
x.getField();  // bang null pointer exception

在Java中,抛出一个已检查的异常RecordNotFoundException,允许编译器提醒客户端处理大小写。

我发现返回空列表的搜索非常方便 - 只需用显示列表中的所有内容填充显示,哦,它是空的,代码“只是工作”。

答案 14 :(得分:2)

对于某些情况,您希望一旦发生故障就会发现故障。

在失败的情况下检查NULL并且不断言(对于程序员错误)或抛出(对于用户或调用者错误)可能意味着后来的崩溃更难以追踪,因为找不到原始的奇怪案例。

此外,忽略错误可能会导致安全漏洞。也许无效是因为缓冲区被覆盖等等。现在,您没有崩溃,这意味着开发人员有机会在您的代码中执行。

答案 15 :(得分:1)

这个主题背后的基本思想是防御性地编程。也就是说,代码对抗意外。 有一系列不同的回复:

Adamski 建议查看Null对象模式,该回复即被投票支持该建议。

Michael Valenty 还建议了一个命名惯例,告诉开发人员可能会有什么预期。 ZeroConcept 建议正确使用Exception,如果这是NULL的原因。 还有其他人。

如果我们制定&#34;规则&#34;我们总是希望进行防御性编程,然后我们可以看到这些建议是有效的。

但我们有2个开发方案。

课程&#34;撰写&#34;由开发人员:作者

课程&#34;消费&#34;另一个(也许)开发人员:开发人员

无论某个类是否为具有返回值的方法返回NULL, 开发人员需要测试对象是否有效。

如果开发人员不能这样做,那么该类/方法不是确定性的。 也就是说,如果&#34;方法调用&#34;获得该对象不会做什么&#34;广告&#34; (例如getEmployee)它违反了合同。

作为一个班级的作者,我总是希望在创建方法时保持善良和防御(和确定性)。

因此,需要检查NULL或NULL OBJECT(例如if(employee as NullEmployee.ISVALID)) 并且可能需要与Employees集合一起发生,那么null对象方法是更好的方法。

但我也喜欢 Michael Valenty 建议命名必须返回null的方法,例如getEmployeeOrNull。

抛出异常的作者是删除开发人员测试对象有效性的选择, 这对于一组对象非常糟糕,并迫使开发人员进行异常处理  分支他们的消费代码时。

作为一名消费类的开发人员,我希望作者能够让我能够避免或编写null的情况  他们的班级/方法可能会面临。

因此,作为开发人员,我会针对方法中的NULL进行防御性编程。 如果作者给了我一个总是返回一个对象的契约(NULL OBJECT总是这样做) 并且该对象有一个方法/属性来测试对象的有效性, 然后我会使用该方法/属性继续使用该对象,否则该对象无效 我无法使用它。

底线是类/方法的作者必须提供机制 开发人员可以在他们的防御性编程中使用。也就是说,该方法更清晰。

开发人员应始终使用防御性编程来测试返回对象的有效性 从另一个类/方法。

问候

GregJF

答案 16 :(得分:1)

有时候,返回NULL是正确的做法,但特别是当你处理不同类型的序列(数组,列表,字符串,你有什么)时,返回零长度序列可能更好因为它导致更短,更有希望更容易理解的代码,而不是在API实现者身上花费更多的代码。

答案 17 :(得分:1)

让他们在事后调用另一个方法来确定前一个调用是否为null。 ;-)嘿,这是good enough for JDBC

答案 18 :(得分:0)

对于我的用例,我需要从方法返回一个Map,然后寻找一个特定的键。但是,如果我返回一个空的Map,它将导致NullPointerException,然后返回null而不是一个空的Map不会有太大的不同。  但是从Java8开始,我们可以使用Optional。以上正是引入可选概念的原因。

答案 19 :(得分:0)

如果代码如下:

command = get_something_to_do()

if command:  # if not Null
    command.execute()

如果你有一个伪对象,其execute()方法什么都不做,并且你在适当的情况下返回而不是Null,你不必检查Null情况,而只能这样做:

get_something_to_do().execute()

所以,这里的问题不是在检查NULL与异常之间,而是在调用者必须以不同方式(以任何方式)处理特殊非案例之间。

答案 20 :(得分:0)

嗯,这肯定取决于方法的目的......有时,更好的选择是抛出异常。这完全取决于具体情况。

答案 21 :(得分:0)

  

在开发复杂程序期间可能会出现意外的空函数,就像死代码一样,这种情况表明程序结构存在严重缺陷。

     

null函数或方法通常用作对象框架中可反射函数或可重写方法的默认行为。

Null_function @wikipedia

答案 22 :(得分:0)

其他选项包括: 返回一些表示成功与否(或错误类型)的值,但是如果你只需要指示成功/失败的布尔值,则为失败返回null,并且成功的对象不会更正确,然后返回true / false并通过参数获取对象 其他方法是使用异常来表示失败,但在这里 - 实际上有更多的声音,说这是一种不良的做法(因为使用异常可能很方便,但有很多缺点)。 因此,我个人认为在返回null时没有看到任何错误,表明出现了问题,并在稍后检查(实际上知道你是否成功)。另外,盲目地认为你的方法不会返回NULL,然后将你的代码基于它,可能会导致其他的,有时很难找到的错误(虽然在大多数情况下它会崩溃你的系统:),因为你将参考迟早是0x00000000。)

答案 23 :(得分:-3)

天儿真好,

当您无法创建新对象时返回NULL是许多API的标准做法。

为什么设计不好我不知道。

编辑:对于没有C等例外情况的语言,情况就是这样。多年来它一直是惯例。

HTH

“Avahappy,