返回 null 是不好的设计吗?

发布于 2024-08-02 01:07:06 字数 1432 浏览 5 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(24

软糖 2024-08-09 01:07:06

不返回 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.
}

...比以下更干净:

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

The rationale behind not returning null is that you do not have to check for it and hence your code does not need to follow a different path based on the return value. You might want to check out the Null Object Pattern which provides more information on this.

For example, if I were to define a method in Java that returned a Collection I would typically prefer to return an empty collection (i.e. Collections.emptyList()) rather than null as it means my client code is cleaner; 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.
}

... which is cleaner than:

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.
  }
}
网白 2024-08-09 01:07:06

原因如下。

Robert Martin 的《干净的代码》 中,他写道,当你可以时,返回 null 是糟糕的设计相反,返回空数组。 既然预期结果是一个数组,为什么不呢? 它将使您能够在没有任何额外条件的情况下迭代结果。 如果它是一个整数,也许 0 就足够了,如果它是一个散列,则空散列。 等等。

前提是不强制调用代码立即处理问题。 调用代码可能不想关心它们。 这也是为什么在许多情况下异常比零好。

Here's the reason.

In Clean Code by Robert Martin he writes that returning null is bad design when you can instead return, say, empty array. Since expected result is an array, why not? It'll enable you to iterate over result without any extra conditions. If it's an integer, maybe 0 will suffice, if it's a hash, empty hash. etc.

The premise is to not force calling code to immediately handle issues. Calling code may not want to concern itself with them. That's also why in many cases exceptions is better than nil.

微暖i 2024-08-09 01:07:06

返回 null 的良好用途:

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

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

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

在这些情况下抛出异常更合适,因为:

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

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

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

Good uses of returning null:

  • If null is a valid functional result, for example: FindFirstObjectThatNeedsProcessing() can return null if not found and the caller should check accordingly.

Bad uses: Trying to replace or hide exceptional situations such as:

  • catch(...) and return null
  • API dependency initialization failed
  • Out of disk space
  • Invalid input parameters (programming error, inputs must be sanitized by the caller)
  • etc

In those cases throwing an exception is more adequate since:

  • A null return value provides no meaningful error info
  • The immediate caller most likely cannot handle the error condition
  • There is no guarantee that the caller is checking for null results

However, Exceptions should not be used to handle normal program operation conditions such as:

  • Invalid username/password (or any user-provided inputs)
  • Breaking loops or as non-local gotos
ˉ厌 2024-08-09 01:07:06

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

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

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

Yes, returning NULL is a terrible design, in object-oriented world. In a nutshell, NULL usage leads to:

  • ad-hoc error handling (instead of exceptions)
  • ambiguous semantic
  • slow instead of fast failing
  • computer thinking instead of object thinking
  • mutable and incomplete objects

Check this blog post for a detailed explanation: http://www.yegor256.com/2014/05/13/why-null-is-bad.html. More in my book Elegant Objects, Section 4.1.

稚气少女 2024-08-09 01:07:06

谁说这是糟糕的设计?

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

Who says this is bad design?

Checking for nulls is a common practice, even encouraged, otherwise you run the risk of NullReferenceExceptions everywhere. Its better to handle the error gracefully than throw exceptions when you don't need to.

淡墨 2024-08-09 01:07:06

根据您到目前为止所说的,我认为信息还不够。

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

从 FindFooInBar() 方法返回 null 似乎没问题。

Based on what you've said so far, I think there's not enough information.

Returning null from a CreateWidget()method seems bad.

Returning null from a FindFooInBar() method seems fine.

谈场末日恋爱 2024-08-09 01:07:06

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

Its inventor says it is a billion dollar mistake!

﹂绝世的画 2024-08-09 01:07:06

这取决于您使用的语言。 如果您使用像 C# 这样的语言,其中指示缺少值的惯用方式是返回 null,那么如果您没有值,则返回 null 是一个很好的设计。 或者,在像 Haskell 这样的语言中,在这种情况下通常使用 Maybe monad,那么返回 null 将是一个糟糕的设计(如果可能的话)。

It depends on the language you're using. If you're in a language like C# where the idiomatic way of indicating the lack of a value is to return null, then returning null is a good design if you don't have a value. Alternatively, in languages such as Haskell which idiomatically use the Maybe monad for this case, then returning null would be a bad design (if it were even possible).

彩虹直至黑白 2024-08-09 01:07:06

如果您阅读了所有答案,就会清楚这个问题的答案取决于方法的种类。

首先,当发生异常情况(IO问题等)时,逻辑上会抛出异常。 当某件事确实是异常的时候,可能是针对不同主题的事情。

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

  • 如果有可能返回一个中性值,那就这样做。< br>
    空枚举、字符串等都是很好的例子。
  • 如果不存在这样的中性值,则应返回 null。
    如前所述,假设该方法可能没有结果,因此它不是异常,因此不应抛出异常。 中性值是不可能的(例如:0 并不是一个特别中性的结果,具体取决于程序)

直到我们有一个正式的方法来表示函数可以或不能返回 null,我尝试有一个命名约定来表示.
就像您对预计失败的方法有 TrySomething() 约定一样,我经常将我的方法命名为 SafeSomething( ) 当该方法返回中性结果而不是 null 时。

我对这个名字还不太满意,但想不出更好的名字。 所以我现在就这么做。

If you read all the answers it becomes clear the answer to this question depends on the kind of method.

Firstly, when something exceptional happens (IOproblem etc), logically exceptions are thrown. When exactly something is exceptional is probably something for a different topic..

Whenever a method is expected to possibly have no results there are two categories:

  • If it is possible to return a neutral value, do so.
    Empty enumrables, strings etc are good examples
  • If such a neutral value does not exist, null should be returned.
    As mentioned, the method is assumed to possibly have no result, so it is not exceptional, hence should not throw an exception. A neutral value is not possible (for example: 0 is not especially a neutral result, depending on the program)

Untill we have an official way to denote that a function can or cannot return null, I try to have a naming convention to denote so.
Just like you have the TrySomething() convention for methods that are expected to fail, I often name my methods SafeSomething() when the method returns a neutral result instead of null.

I'm not fully ok with the name yet, but couldn't come up with anything better. So I'm running with that for now.

痕至 2024-08-09 01:07:06

我在这个领域有一个惯例,对我很有帮助

对于单个项目查询:

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

对于集合查询:

  • Get... ​​下找不到匹配的[1]项[1],则该集合为空。

始终返回一个集合,如果在给定某些标准(显式或隐式、在函数名称中或作为参数中给出)的情况

I have a convention in this area that served me well

For single item queries:

  • Create... returns a new instance, or throws
  • Get... returns an expected existing instance, or throws
  • GetOrCreate... returns an existing instance, or new instance if none exists, or throws
  • Find... returns an existing instance, if it exists, or null

For collection queries:

  • Get... always returns a collection, which is empty if no matching[1] items are found

[1] given some criteria, explicit or implicit, given in the function name or as parameters.

情愿 2024-08-09 01:07:06

例外是指特殊情况。

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

Exceptions are for exceptional circumstances.

If your function is intended to find an attribute associated with a given object, and that object does has no such attribute, it may be appropriate to return null. If the object does not exist, throwing an exception may be more appropriate. If the function is meant to return a list of attributes, and there are none to return, returning an empty list makes sense - you're returning all zero attributes.

难以启齿的温柔 2024-08-09 01:07:06

这不一定是一个糟糕的设计——就像许多设计决策一样,这取决于情况。

如果该方法的结果在正常使用中不会产生良好的结果,则返回 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 /* ... */ {
}

It's not necessarily a bad design - as with so many design decisions, it depends.

If the result of the method is something that would not have a good result in normal use, returning null is fine:

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

If there really should always be a non-null result, then it might be better to throw an 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 /* ... */ {
}
窝囊感情。 2024-08-09 01:07:06

如果这样做在某种程度上有意义,则返回 null 是可以的:

public String getEmployeeName(int id){ ..}

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

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

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

It's fine to return null if doing so is meaningful in some way:

public String getEmployeeName(int id){ ..}

In a case like this it's meaningful to return null if the id doesn't correspond to an existing entity, as it allows you to distinguish the case where no match was found from a legitimate error.

People may think this is bad because it can be abused as a "special" return value that indicates an error condition, which is not so good, a bit like returning error codes from a function but confusing because the user has to check the return for null, instead of catching the appropriate exceptions, e.g.

public Integer getId(...){
   try{ ... ; return id; }
   catch(Exception e){ return null;}
}
遇到 2024-08-09 01:07:06

对于某些场景,您希望在故障发生时立即注意到它。

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

此外,忽略错误可能会导致安全漏洞。 也许无效性来自于缓冲区被覆盖等事实。 现在,您没有崩溃,这意味着利用者有机会在您的代码中执行。

For certain scenarios, you want to notice a failure as soon as it happens.

Checking against NULL and not asserting (for programmer errors) or throwing (for user or caller errors) in the failure case can mean that later crashes are harder to track down, because the original odd case wasn't found.

Moreover, ignoring errors can lead to security exploits. Perhaps the null-ness came from the fact that a buffer was overwritten or the like. Now, you are not crashing, which means the exploiter has a chance to execute in your code.

半城柳色半声笛 2024-08-09 01:07:06

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

我看到两种情况:

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

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

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

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

我相信 return null 可能不如替代方案好,因为它要求客户端记住检查 null,程序员忘记了,并且

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

在 Java 中编写代码时,抛出一个检查异常 RecordNotFoundException 允许编译器提醒客户端处理情况。

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

What alternatives do you see to returning null?

I see two cases:

  • findAnItem( id ). What should this do if the item is not found

In this case we could: Return Null or throw a (checked) exception (or maybe create an item and return it)

  • listItemsMatching (criteria) what should this return if nothing is found?

In this case we could return Null, return an empty list or throw an Exception.

I believe that return null may be less good than the alternatives becasue it requires the client to remember to check for null, programmers forget and code

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

In Java, throwing a checked exception, RecordNotFoundException, allows the compiler to remind the client to deal with case.

I find that searches returning empty lists can be quite convenient - just populate the display with all the contents of the list, oh it's empty, the code "just works".

只是一片海 2024-08-09 01:07:06

让他们在事后调用另一个方法来确定之前的调用是否为空。 ;-) 嘿,这是 对于 JDBC 来说足够好了

Make them call another method after the fact to figure out if the previous call was null. ;-) Hey, it was good enough for JDBC

糖果控 2024-08-09 01:07:06

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

Well, it sure depends of the purpose of the method ... Sometimes, a better choice would be to throw an exception. It all depends from case to case.

云淡月浅 2024-08-09 01:07:06

有时,返回 NULL 是正确的做法,但特别是当您处理不同类型的序列(数组、列表、字符串等)时,返回零长度序列可能更好,因为它导致代码更短且更容易理解,同时 API 实现者无需进行更多编写。

Sometimes, returning NULL is the right thing to do, but specifically when you're dealing with sequences of different sorts (arrays, lists, strings, what-have-you) it is probably better to return a zero-length sequence, as it leads to shorter and hopefully more understandable code, while not taking much more writing on API implementer's part.

失与倦" 2024-08-09 01:07:06

该线程背后的基本思想是防御性编程。 也就是说,针对意外情况进行编码。
有一系列不同的回复:

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

Michael Valenty 还建议采用命名约定来告诉开发人员可能会发生什么。
ZeroConcept 建议正确使用 Exception(如果这是 NULL 的原因)。
和别的。

如果我们制定“规则”,我们总是想要进行防御性编程,那么我们可以看到这些建议是有效的。

但我们有两种开发场景。

由开发人员“创作”的类:作者

由另一个(可能是)开发人员“使用”的类:开发人员

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

如果开发人员不能做到这一点,那么该类/方法就不是确定性的。
也就是说,如果获取对象的“方法调用”没有执行它“宣传”的操作(例如 getEmployee),那么它就违反了契约。

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

因此,需要检查 NULL 或 NULL OBJECT(例如 if(employee as NullEmployee.ISVALID))
并且这可能需要在员工集合中发生,那么空对象方法是更好的方法。

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

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

作为使用该类的开发人员,我希望作者能够让我能够避免或针对 null 情况进行编程
他们的类/方法可能面临的。

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

底线是类/方法的作者必须提供机制
开发人员可以在防御性编程中使用它。 即该方法的意图更加明确。

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

问候

格雷格·JF

The base idea behind this thread is to program defensively. That is, code against the unexpected.
There is an array of different replies:

Adamski suggests looking at Null Object Pattern, with that reply being up voted for that suggestion.

Michael Valenty also suggests a naming convention to tell the developer what may be expected.
ZeroConcept suggests a proper use of Exception, if that is the reason for the NULL.
And others.

If we make the "rule" that we always want to do defensive programming then we can see that these suggestions are valid.

But we have 2 development scenarios.

Classes "authored" by a developer: The Author

Classes "consumed" by another(maybe) developer: the Developer

Regardless of whether a class returns NULL for methods with a return value or not,
the Developer will need to test if the object is valid.

If the developer cannot do this, then that Class/method is not deterministic.
That is, if the "method call" to get the object does not do what it "advertises" (eg getEmployee) it has broken the contract.

As an author of a class, I always want to be as kind and defensive ( and deterministic) when creating a method.

So given that either NULL or the NULL OBJECT (eg if(employee as NullEmployee.ISVALID)) needs to be checked
and that may need to happen with a collection of Employees, then the null object approach is the better approach.

But I also like Michael Valenty's suggestion of naming the method that MUST return null eg getEmployeeOrNull.

An Author who throws an exception is removing the choice for the developer to test the object's validity,
which is very bad on a collection of objects, and forces the developer into exception handling
when branching their consuming code.

As a developer consuming the class, I hope the author gives me the ability to avoid or program for the null situation
that their class/methods may be faced with.

So as a developer I would program defensively against NULL from a method.
If the author has given me a contract that always returns a object (NULL OBJECT always does)
and that object has a method/property by which to test the validity of the object,
then I would use that method/property to continue using the object, else the object is not valid
and I cannot use it.

Bottom line is that the Author of the Class/Methods must provide mechanisms
that a Developer can use in their defensive programming. That is, a clearer intention of the method.

The Developer should always use defensive programming to test the validity of the objects returned
from another class/method.

regards

GregJF

情丝乱 2024-08-09 01:07:06

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

Other options to this, are:
returning some value that indicates success or not (or type of an error), but if you just need boolean value that will indicate success / fail, returning null for failure, and an object for success wouldn't be less correct, then returning true/false and getting the object through parameter.
Other approach would to to use exception to indicates failures, but here - there are actually many more voices, that say this is a BAD practice (as using exceptions may be convenient but has many disadvantages).
So I personally don't see anything bad in returning null as indication that something went wrong, and checking it later (to actually know if you have succeeded or not). Also, blindly thinking that your method will not return NULL, and then base your code on it, may lead to other, sometimes hard to find, errors (although in most cases it will just crash your system :), as you will reference to 0x00000000 sooner or later).

╰ゝ天使的微笑 2024-08-09 01:07:06

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

空函数或方法通常用作对象框架中可重新向量函数或可重写方法的默认行为。

Null_function @wikipedia

Unintended null functions can arise during the development of a complex programs, and like dead code, such occurrences indicate serious flaws in program structures.

A null function or method is often used as the default behavior of a revectorable function or overrideable method in an object framework.

Null_function @wikipedia

魂ガ小子 2024-08-09 01:07:06

如果代码类似于:

command = get_something_to_do()

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

如果您有一个虚拟对象,其 execute() 方法不执行任何操作,并且在适当的情况下返回该对象而不是 Null,则您不必检查 Null 情况,而只需执行以下操作:

get_something_to_do().execute()

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

If the code is something like:

command = get_something_to_do()

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

If you have a dummy object whose execute() method does nothing, and you return that instead of Null in the appropriate cases, you don't have to check for the Null case and can instead just do:

get_something_to_do().execute()

So, here the issue is not between checking for NULL vs. an exception, but is instead between the caller having to handle special non-cases differently (in whatever way) or not.

ˇ宁静的妩媚 2024-08-09 01:07:06

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

For my use case I needed to return a Map from method and then looking for a specific key. But if I return an empty Map, then it will lead to NullPointerException and then it wont be much different returning null instead of an empty Map.
But from Java8 onward we could use Optional. The above is the very reason Optional concept was introduced.

陌生 2024-08-09 01:07:06

天哪,

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

为什么它的设计很糟糕,我不知道。

编辑:对于没有例外的语言来说确实如此,例如 C,多年来它一直是惯例。

HTH

'阿瓦快乐,

G'day,

Returning NULL when you are unable to create a new object is standard practise for many APIs.

Why the hell it's bad design I have no idea.

Edit: This is true of languages where you don't have exceptions such as C where it has been the convention for many years.

HTH

'Avahappy,

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文