返回null坏devise?
我听到一些声音说检查从方法返回的空值是不好的devise。 我想听听这个原因。
伪代码:
variable x = object.method() if (x is null) do something
不返回null的基本原理是你不必检查它,因此你的代码不需要遵循基于返回值的不同path 。 您可能想要查看提供更多信息的空对象模式 。
例如,如果我要在Java中定义一个返回集合的方法,我通常更喜欢返回一个空的集合(即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. }
比…更清洁的
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. } }
这是原因。
在罗伯特·马丁(Robert Martin)的Clean Code中,他写道,当你可以返回空数组时,返回null是不好的devise。 由于预期的结果是一个数组,为什么不呢? 它将使您能够在没有任何额外条件的情况下迭代结果。 如果它是一个整数,也许0就足够了,如果它是一个散列,空散列。 等等
前提是不要强制调用代码来立即处理问题。 调用代码可能不想关心他们自己。 这也是为什么在许多情况下,例外情况比零好。
返回null的好用处:
- 如果null是有效的function结果,例如:FindFirstObjectThatNeedsProcessing()如果未find,则返回null,调用者应该相应地进行检查。
不好的用途:试图replace或隐藏特殊情况,例如:
- catch(…)并返回null
- API依赖项初始化失败
- 磁盘空间不足
- input参数无效(编程错误,input必须由调用者清理)
- 等等
在这些情况下抛出一个exception是更充分的,因为:
- 空返回值不提供有意义的错误信息
- 直接调用者很可能无法处理错误情况
- 不能保证调用者正在检查空结果
但是,不应该使用exception来处理正常的程序操作条件,例如:
- 无效的用户名/密码(或任何用户提供的input)
- 打破循环或作为非本地gotos
谁说这是不好的devise?
检查空值是一种常见的做法,甚至是被鼓励的,否则在任何地方都会冒NullReferenceExceptions的风险。 它更好地处理错误,而不是在不需要的时候抛出exception。
是的,在面向对象的世界里,返回NULL是一个糟糕的devise 。 简而言之,NULL用法导致:
- 临时error handling(而不是例外)
- 模棱两可的语义
- 慢而不是快速失败
- 电脑思维而不是对象思维
- 可变和不完整的对象
检查这个博客文章的详细解释: http : //www.yegor256.com/2014/05/13/why-null-is-bad.html 。 更多在我的书优雅的对象 ,4.1节。
根据你到目前为止所说的话,我认为没有足够的信息。
从CreateWidget()方法返回null看起来很糟糕。
从FindFooInBar()方法返回null似乎很好。
它的发明者说这是十亿美元的错误!
这取决于你使用的语言。 如果你使用的是像C#这样的语言,那么表示缺less值的惯用方法是返回null,那么如果没有值,则返回null是一个好的devise。 或者,在Haskell这种惯用的Maybe monad语言的语言中,返回null将是一个糟糕的devise(如果甚至可能的话)。
如果这样做有意义,可以返回null:
public String getEmployeeName(int id){ ..}
在这种情况下,如果id与现有实体不一致,则返回空值是有意义的,因为它允许您区分未发现匹配与合法错误的情况。
人们可能会认为这是不好的,因为它可能被滥用作为一个“特殊的”返回值,表示错误条件,这是不是很好,有点像从函数返回错误代码,但令人困惑,因为用户必须检查返回null,而不是捕捉适当的例外,例如
public Integer getId(...){ try{ ... ; return id; } catch(Exception e){ return null;} }
这不一定是一个糟糕的devise – 就像这么多的devise决定一样,这取决于。
如果方法的结果是在正常使用中没有好的结果,则返回null是很好的:
object x = GetObjectFromCache(); // return null if it's not in the cache
如果真的应该总是有一个非空的结果,那么抛出exception可能会更好:
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 /* ... */ { }
如果你阅读所有的答案,就很清楚,这个问题的答案取决于方法的types。
首先,当exception情况发生时(IO问题等),会引发逻辑exception。 什么东西是特殊的时候可能是一个不同的话题..
每当一个方法预计可能没有结果时,有两个类别:
- 如果有可能返回一个中性值,那就这样做 。
空的enumrables,string等是很好的例子 - 如果这个中性值不存在,则返回null 。
如前所述,该方法被认为可能没有结果,所以它不是例外,因此不应该抛出exception。 一个中性值是不可能的(例如:0不是一个中立的结果,取决于程序)
直到我们有一个官方的方式来表示一个函数可以或不可以返回null,我尝试有一个命名约定来表示。
就像你对那些预期会失败的方法使用Try Something()约定一样,当方法返回一个中立的结果而不是null时,我经常将我的方法命名为Safe Something() 。
我还没有完全确定这个名字,但不能拿出更好的东西。 所以我现在正在运行。
你看到什么替代方法返回null?
我看到两种情况:
- findAnItem(id)。 如果找不到该项目,该怎么办?
在这种情况下,我们可以:返回Null或抛出(检查)exception(或者创build一个项目并返回)
- listItemsMatching(条件)如果没有发现什么,这应该返回什么?
在这种情况下,我们可以返回Null,返回一个空列表或抛出一个Exception。
我相信返回null可能不如替代品好,因为它需要客户端记住检查null,程序员忘记和代码
x = find(); x.getField(); // bang null pointer exception
在Java中,抛出一个检查exceptionRecordNotFoundException,允许编译器提醒客户端处理大小写。
我发现,返回空列表的search可以非常方便 – 只需填充列表中的所有内容的显示,哦,它是空的,代码“正常工作”。
例外情况是例外情况。
如果函数的目的是查找与给定对象相关联的属性,并且该对象没有这样的属性,则返回null可能是合适的。 如果对象不存在,抛出exception可能更合适。 如果函数的目的是返回一个属性列表,而且没有返回值,那么返回一个空的列表是有意义的 – 你将返回所有的零属性。
我在这方面有一个公约,这对我很好
对于单项查询:
-
Create...
返回一个新的实例, 或抛出 -
Get...
返回一个预期的现有实例, 或抛出 -
GetOrCreate...
返回一个现有的实例,或者新的实例,如果不存在或抛出 -
Find...
返回一个存在的实例, 或者返回null
对于收集查询:
-
Get...
总是返回一个集合,如果没有find匹配的[1]项目,这个集合是空的
[1]给出了一些标准,显式或隐式,在函数名中给出或作为参数。
对于某些情况,只要发生故障就要注意失败。
在失败情况下检查NULL和不断言(对于程序员错误)或抛出(对于用户或调用者错误)可能意味着后来的崩溃更难追查,因为没有find原始的奇怪情况。
而且,忽略错误会导致安全漏洞。 也许是因为缓冲区被覆盖等原因而导致无效。 现在,你不会崩溃,这意味着利用者有机会在你的代码中执行。
有时,返回NULL是正确的做法,但是特别是在处理不同types的序列时(数组,列表,string,还有什么),最好返回一个零长度的序列,因为它导致更短,希望更易于理解的代码,而不会在API实现者方面做更多的工作。
让他们调用另一个方法之后,找出以前的调用是否为空。 ;-)嘿,这对于JDBC来说已经足够了
这个线程背后的基本思想是防守编程。 那就是代码反对意外。 有一系列不同的答复:
Adamskibuild议查看空对象模式,并且对这个build议投了赞成票。
迈克尔·瓦伦蒂(Michael Valenty)也提出了一个命名约定来告诉开发者可能会有什么样 如果这是NULL的原因, ZeroConceptbuild议正确使用Exception。 和别的。
如果我们制定我们总是想做防御性编程的“规则”,那么我们可以看到这些build议是有效的。
但是我们有两个开发场景。
开发人员撰写的类:作者
另一个(也许)开发者“消费”的类:开发者
无论类是否返回具有返回值的方法的NULL,开发人员都需要testing该对象是否有效。
如果开发人员不能这样做,那么这个类/方法是不确定的。 也就是说,如果获得对象的“方法调用”没有做“广告”(比如getEmployee),那就违反了合同。
作为一个class级的作者,我总是希望在创build一个方法的时候能够performance出善意和防御(和确定性)。
因此,如果需要检查NULL或NULL OBJECT(例如,如果(雇员为NullEmployee.ISVALID)),并且可能需要与Employees集合一起发生,则空对象方法是更好的方法。
但是我也喜欢Michael Valenty提出的命名方法,比如必须返回null,例如getEmployeeOrNull。
引发exception的作者将删除开发人员的select,以testing对象集合的有效性,这对对象集合是非常不利的,并且在分支消费代码时强制开发人员进行exception处理。
作为一名开发人员,我希望作者能让我避免或计划他们的类/方法可能面临的空情况。
所以作为一名开发人员,我会防御性地从一个方法中对NULL进行编程。 如果作者给了我一个总是返回一个对象的合约(NULL OBJECT总是这样做),并且该对象有一个方法/属性来testing对象的有效性,那么我将使用该方法/属性继续使用该对象,否则该对象是无效的,我不能使用它。
底线是,类/方法的作者必须提供开发人员可以在其防御性编程中使用的机制。 也就是说,这个方法的意图更清楚了。
开发人员应该始终使用防御性编程来testing从另一个类/方法返回的对象的有效性。
问候
GregJF
其他的选项是:返回一些表示成功与否的值(或者错误的types),但是如果你只是需要布尔值来表示成功/失败,返回null表示失败,成功的对象不会不太正确,然后返回true / false并通过参数获取对象。
其他的方法是使用exception来表示失败,但在这里 – 实际上有更多的声音,说这是一个不好的做法(因为使用例外可能是方便的,但有许多缺点)。
所以我个人没有看到返回null的任何错误,表明出了什么问题,并在稍后检查(实际上是否知道你是否成功了)。 另外,一味地认为你的方法不会返回NULL,然后把你的代码放在它上面,可能会导致其他的,有时很难find的错误(虽然在大多数情况下,它只会使你的系统崩溃:),你会参考0x00000000迟早)。
在复杂的程序开发过程中,可能会出现意想不到的空函数,像死代码一样,这种情况表明程序结构存在严重的缺陷。
在对象框架中,经常使用空函数或方法作为可触及函数或可覆盖方法的默认行为。
Null_function @维基百科
那么,这肯定取决于方法的目的…有时候,更好的select是抛出exception。 这一切都依情况而定。
如果代码是这样的:
command = get_something_to_do() if command: # if not Null command.execute()
如果你有一个其execute()方法什么都不做的虚拟对象,并且你在适当的情况下返回它而不是Null,你不必检查Null情况,而只需要做:
get_something_to_do().execute()
所以,这里的问题不是在检查NULL和exception之间,而是在调用者必须以不同的方式(以任何方式)处理特殊的非情况之间进行。
天儿真好,
当您无法创build新对象时返回NULL是许多API的标准做法。
为什么这是糟糕的devise我不知道。
编辑:这是没有例外的语言,如C多年来的惯例。
HTH
“Avahappy,