代码之家  ›  专栏  ›  技术社区  ›  koen

是否返回空的坏设计?[关闭]

  •  117
  • koen  · 技术社区  · 15 年前

    我听到一些声音说检查方法返回的空值是错误的设计。我想听听这方面的原因。

    pseudocode:

    variable x = object.method()
    if (x is null) do something
    
    24 回复  |  直到 6 年前
        1
  •  191
  •   Adamski    15 年前

    不返回空值的基本原理是您不必检查它,因此您的代码不需要 走不同的路 基于返回值。你可能想看看 Null Object Pattern 它提供了更多关于这个的信息。

    例如,如果要在Java中定义返回集合的方法,通常我会返回一个空集合(即 Collections.emptyList() )而不是空,因为这意味着我的客户机代码更干净;例如

    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.
      }
    }
    
        2
  •  66
  •   Max Chernyak    15 年前

    这就是原因。

    Clean Code by Robert Martin 他写道,当您可以返回空数组时,返回空值是错误的设计。既然预期的结果是一个数组,为什么不呢?它将使您能够在没有任何额外条件的情况下迭代结果。如果它是一个整数,也许0就足够了,如果它是一个散列,空散列。等。

    前提是不要强迫调用代码立即处理问题。调用代码可能不想与它们有关。这也是为什么在许多情况下例外情况比零好的原因。

        3
  •  35
  •   ZeroConcept    15 年前

    返回空值的良好用途:

    • 如果null是有效的 功能性 结果,例如:如果找不到findfirstobjectthaneedsprocessing(),则可以返回空值,调用方应进行相应的检查。

    不良用途:试图替换或隐藏特殊情况,如:

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

    在这些情况下,引发异常更为充分,因为:

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

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

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

    是的,返回空值是 可怕的设计 ,在面向对象的世界中。简而言之,空用法导致:

    • 特殊错误处理(而不是异常)
    • 语义模糊
    • 慢而不是快失败
    • 计算机思维而不是物体思维
    • 可变和不完整对象

    查看此日志以获取详细解释: http://www.yegor256.com/2014/05/13/why-null-is-bad.html .更多在我的书中 Elegant Objects ,第4.1节。

        5
  •  22
  •   Brandon    15 年前

    谁说这个设计不好?

    检查空值是一种常见的实践,甚至鼓励这样做,否则到处都有发生空引用异常的风险。在不需要时,优雅地处理错误比抛出异常要好。

        6
  •  17
  •   jcollum    15 年前

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

    从CreateWidget()方法返回空值似乎不正确。

    从findfooinbar()方法返回空值似乎很好。

        7
  •  12
  •   Bahadır Yağan    15 年前

    其发明人 says 这是一个10亿美元的错误!

        8
  •  10
  •   Greg Beech    15 年前

    这取决于你使用的语言。如果您使用的语言类似于C,表示缺少值的惯用方法是返回空值,那么如果您没有值,返回空值是一种很好的设计。或者,在诸如haskell这样的语言中,在本例中习惯性地使用了makemonad,那么返回null将是一种糟糕的设计(如果可能的话)。

        9
  •  5
  •   Boris Callens    15 年前

    如果你读了所有的答案,你就会明白这个问题的答案取决于方法的种类。

    首先,当发生异常(ioproblem等)时,逻辑异常被抛出。当某个事物确实是特殊的时候,它可能是另一个主题的事物。

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

    • 如果可以返回 中性值,这样做 .
      空的枚举表、字符串等是很好的例子
    • 如果不存在这种中性值, 应返回空值 .
      如前所述,假定该方法可能没有结果,因此它不是异常的,因此不应引发异常。中性值不可能(例如:0不是特别中性的结果,取决于程序)

    直到我们有一种正式的方法来表示一个函数可以或不能返回空值,我尝试用一种命名约定来表示空值。
    尝试 某物() 对于预期会失败的方法的约定,我经常命名我的方法 保险柜 某物() 当方法返回中性结果而不是空值时。

    我对这个名字还不太满意,但没能想出更好的名字。所以我现在就用它跑。

        10
  •  3
  •   HyperHacker    15 年前

    例外适用于 例外 所有情况。

    如果函数要查找与给定对象关联的属性,而该对象没有此类属性,则返回空值可能是合适的。如果对象不存在,则引发异常可能更合适。如果函数要返回一个属性列表,并且没有要返回的属性,那么返回一个空列表是有意义的——您返回的是所有零属性。

        11
  •  3
  •   Steve B.    15 年前

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

    public String getEmployeeName(int id){ ..}
    

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

    人们可能认为这是不好的,因为它可能被滥用为表示错误条件的“特殊”返回值,这不太好,有点像从函数返回错误代码,但由于用户必须检查返回值是否为空,而不是捕获适当的异常,例如。

    public Integer getId(...){
       try{ ... ; return id; }
       catch(Exception e){ return null;}
    }
    
        12
  •  3
  •   Michael Burr    15 年前

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

    如果方法的结果在正常使用中不会有好的结果,则返回空值是可以的:

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

    如果确实应该有一个非空的结果,那么最好抛出一个异常:

    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 /* ... */ {
    }
    
        13
  •  3
  •   Johann Gerell    10 年前

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

    对于单项查询:

    • Create... 返回新实例,
    • Get... 返回预期的现有实例, 或投掷
    • GetOrCreate... 返回现有实例,如果不存在则返回新实例, 或投掷
    • Find... 返回现有实例(如果存在), null

    对于集合查询:

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

    [1]给定了一些条件,显式或隐式,在函数名或参数中给出。

        14
  •  2
  •   Drew Hoskins    15 年前

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

    检查是否为空,在失败的情况下不断言(针对程序员错误)或抛出(针对用户或调用方错误),可能意味着以后的崩溃更难跟踪,因为没有找到原始的奇数情况。

    此外,忽略错误可能导致安全漏洞。可能是因为缓冲区被覆盖之类的事实。现在,你是 崩溃,这意味着攻击者有机会在代码中执行。

        15
  •  2
  •   djna    15 年前

    对于返回空值,您看到了什么替代方法?

    我看到两个案例:

    • 查找项(ID)。如果找不到该项目该怎么办

    在这种情况下,我们可以:返回空值或抛出(选中的)异常(或者创建一个项并返回它)

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

    在这种情况下,我们可以返回空值、返回空列表或引发异常。

    我相信返回空值可能不如其他方法好,因为它要求客户端记住检查空值,程序员忘记并编码

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

    在爪哇中,抛出一个已检查的异常,ReordOrdFunExtExpExt,允许编译器提醒客户端处理案例。

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

        16
  •  1
  •   Vatine    15 年前

    有时,返回空值是正确的做法,但特别是当您处理不同类型的序列(数组、列表、字符串,您拥有什么)时,返回零长度序列可能会更好,因为它会导致代码更短,并且希望更易于理解,同时不会对API实现者进行更多的编写。

        17
  •  1
  •   David Plumpton    15 年前

    让他们在事实之后调用另一个方法,以确定上一个调用是否为空。嘿,是的。 good enough for JDBC

        18
  •  1
  •   GregJF    10 年前

    这个线程背后的基本思想是进行防御编程。也就是说,用代码来防止意外。 有一系列不同的答复:

    亚当斯基 建议查看空对象模式,并对该建议的回复进行投票。

    米迦勒·瓦伦蒂 还建议使用命名约定来告诉开发人员可能需要什么。 零概念 如果这是空值的原因,则建议正确使用异常。 以及其他。

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

    但是我们有两个开发场景。

    开发人员“编写”的类:作者

    其他(可能)开发人员“消费”的类:开发人员

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

    如果开发人员不能这样做,那么该类/方法就不是确定性的。 也就是说,如果获取对象的“方法调用”不执行它“通告”的操作(例如getEmployee),那么它就违反了合同。

    作为一个类的作者,我总是希望在创建一个方法时表现出友好和防御性(以及确定性)。

    因此,如果需要检查空对象或空对象(例如if(employee as null employee.isvalid))。 对于一个雇员集合可能需要这样做,那么空对象方法是更好的方法。

    但我也喜欢 迈克尔·瓦伦蒂的 建议命名必须返回空值的方法,例如GetEmployeeOrNull。

    引发异常的作者正在删除开发人员测试对象有效性的选择, 这在对象集合上非常糟糕,并强制开发人员进行异常处理。 当分支它们的消费代码时。

    作为一个使用类的开发人员,我希望作者能够给我避免或为空情况编程的能力。 他们的类/方法可能面临的问题。

    因此,作为一个开发人员,我会对方法中的空值进行防御性的编程。 如果作者给了我一个始终返回对象的协定(空对象总是这样做) 并且该对象有一个方法/属性,用于测试该对象的有效性, 然后我将使用该方法/属性继续使用该对象,否则该对象无效。 我不能用它。

    归根结底,类/方法的作者必须提供机制 开发人员可以在防御编程中使用。也就是说,这种方法的意图更加明确。

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

    当做

    格雷夫

        19
  •  0
  •   Marcin Deptuła    15 年前

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

        20
  •  0
  •   Juicy Scripter    15 年前

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

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

    Null_function @wikipedia

        21
  •  0
  •   Denis Biondic    15 年前

    当然,这取决于方法的目的…有时,更好的选择是抛出一个异常。这完全取决于具体情况。

        22
  •  0
  •   Anon    15 年前

    如果代码类似于:

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

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

    get_something_to_do().execute()
    

    因此,这里的问题不是在检查空值和异常之间,而是在调用者必须以不同的方式(无论以何种方式)处理特殊的非情况之间。

        23
  •  0
  •   Aman Gupta    6 年前

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

        24
  •  -4
  •   Rob Wells    15 年前

    国庆节,

    在无法创建新对象时返回空值是许多API的标准实践。

    为什么它的设计不好?我不知道。

    编辑: 这适用于没有例外的语言,如C语言,它已经成为多年的惯例。

    高温高压

    “快乐,