【问题标题】:Is Java object containing null variables an anti-pattern? If so which one? [closed]包含空变量的 Java 对象是反模式吗?如果有,是哪一个? [关闭]
【发布时间】:2013-08-05 13:57:34
【问题描述】:

如果我查询一个对象说一个 Animal 并且返回的对象不是 null 但包含 null 变量,那是错误的吗?例如我可以打电话给animal.getDeathDate();并且由于它还没有死,它返回null。对于 Turtle,getFlightSpeed() 将返回 null,因为在添加 Turtle 火箭包之前它无法飞行。等等等等。

我认为这是一种糟糕的处理方式,因为在调用对象的方法以验证它们是否包含非空值时,它通常会导致需要进行大量空值检查。是否有任何相关信息的链接可以进一步告知我自己和我的同事?

【问题讨论】:

  • 您可能需要 Null Object 模式。
  • 我读到了空对象模式,但我不确定它是否解决了具有空变量的对象。我认为这与是否应该从查询方法而不是数据对象返回 null 或对象有关。
  • 空对象模式基本上适用于每个null。保证非空值使代码更容易。
  • 好的,谢谢,我想我会重定向到那个,唯一的问题是似乎有很多关于它的争论。我希望有一个更明确的答案,并达成共识。您可以添加您的评论作为答案吗?

标签: java anti-patterns


【解决方案1】:

null 通常是模棱两可的。该字段是尚未初始化还是没有任何值?

为未初始化/不相关的字段设置一些预定义常量通常会更好。

更好的是让一个班级只负责一项职责。像getFlightSpeed() 这样的方法不应该被继承,而是来自实现一个接口(不过,像getDeathDate() 这样的方法应该在Animal 仍然存在时返回一个预定义的常量)。

As brought by google-guava docs:

Doug Lea(java.util.concurrent 包的作者)说Null s**ks

还有sir C. A. R. Hoarenull 参考的发明者说:I call it my billion-dollar mistake

这是一个宽阔的肩膀。

【讨论】:

  • 这是我的看法,我希望有一些参考资料来帮助支持它。只能通过 Google 围绕空对象模式展开一场相当大的辩论。
  • @FooBar 添加了一些参考。
  • 你对getDeathDate()返回有何建议?
  • @Gabe 通常您可以在Animal 中声明public static final Date NOT_DEAD = // some Date。在这种特殊情况下,如果所有日期都可行,则可能不适用。所以我会使用Optional
  • 我想Optional 会起作用,尽管除了给你两种不同类型的空值之外,很难看出它比null 更好。您如何决定何时使用Optional
【解决方案2】:

返回null 表示活着的动物的死亡日期是完全合理的,但在这种情况下,我发现最好提供布尔死亡检查:

public boolean isDead() {
    return deathDate != null;
}

这提供了一种合理的方法来检查实例的死亡情况,而无需对属性进行笨拙的 null 检查:

// this is ugly and exposes the choice of the value of the field when alive
if (animal.getDeathDate() != null) {
    // the animal is dead
}

使用isDead() 方法,您将有权执行此操作:

public Date getDeathDate() {
    if (deathDate == null)
        throw new IllegalStateException("Death has not occurred");
    return deathDate;
}

关于乌龟的飞行速度,您可以采用相同的方法,尽管我认为您的类设计存在问题 - 并非所有动物都会飞,因此 Animal 类不应该有 getFlyingSpeed() 方法。

相反,使用这样的东西:

interface Flyer {
    Integer getFlightSpeed();
}

class Animal {}

class Turtle extends Animal {}

class Eagle extends Animal implements Flyer {
    public Integer getFlightSpeed() {
         //
    }
}

【讨论】:

  • 对于getFlyingSpeed的情况,我们可以在Animal类中有一个实例变量引用Flyer类型而不是实现一个接口(在这个接口中有两个方法可以飞行和获得飞行速度)?
  • 拥有 canFly() 是不必要或不可取的,因为 a) 您可以使用 if (animal instaceof Flyer),并且 b) 您无论如何都需要强制转换为 Flyer 才能访问 Flyer 方法,所以您已经知道它是传单,因为您必须先进行一次检查,然后才能施放。
【解决方案3】:

Null 有时是表示对象缺少特定属性的一种完全合理的方式。

但是,允许单个检查很有用。

对于数组或列表,最好有一个始终为非空但可以指向空列表的变量。否则,就需要检查变量是否为非空以及列表是否有成员。

【讨论】:

    【解决方案4】:

    我的主要空值检查规则是永远不要放置空值而不是列表或数组。

    空列表和数组更能更好地表达它们的真实含义。

    【讨论】:

      【解决方案5】:

      我不认为这是错误的。打个比方,想想NULL in SQL 的语义:

      记住 NULL 意味着什么的一个好方法是记住 信息,“缺乏价值”与“价值 零”;同样,“缺乏答案”与“一个 否”。

      在 Java 中应用相同的逻辑是完全有效的。

      为了使原始类型可以为空,请查看nullable types in C#。将 Nullable 实现为 Java 泛型应该很容易。

      【讨论】:

        【解决方案6】:

        这只是我的观点,但您的示例听起来确实像一个反模式(作为退化的空对象),因为您需要对“空对象”上的方法返回的内容进行空检查。如果您根本不调用这些 getter,那么您的示例将是正确的。

        使用 null 对象的想法是您根本不需要执行 null 检查,因此如果您的 getter 返回其他 null 对象,或者方法使用 tell-don't-ask 方法(并且只返回 void)。

        【讨论】:

          【解决方案7】:

          Null 很好,但你应该尽量避免使用默认值

          例如 应该返回空列表而不是 null 枚举数据类型应该包含 UNKNOWN

          以上假设使 API 使用者的生活变得轻松

          在您的示例中,我会从 animal.getDeathDate 返回 null,因为我想不出任何合适的默认值。

          我将提供方便的方法 animal.isDead 返回真/假

          对于 getFlightSpeed(),我会为您的情况返回 0

          【讨论】:

            【解决方案8】:

            空对象模式确实可以用于空变量的情况。 在 Turtle.getFlightSpeed() 的情况下,您可以抽象出 SPEED 概念(可能是接口或抽象类),并让一个 NULL 对象实现“无法飞行”的场景。 这将有助于将默认行为分配给 Turtle 类。 在 animal.getDeathDate() 的情况下返回 null 似乎很好

            【讨论】:

              【解决方案9】:

              我认为对于 animal.getDeathDate() 如果动物还活着则返回 null 是正确的做事方式。您将始终需要特殊代码来处理 2 种情况:动物活着和动物死亡,如果动物还活着,则没有可以返回的有用日期。

              对于 getFlightSpeed(),情况可能有所不同。我不确切知道它返回什么,但我们只是举个例子,想象它只是返回速度为 m/s

              在这种情况下返回 0(或具有相同效果的对象)将非常有意义,因为它确实描述了飞行速度。

              【讨论】:

              • “我认为对于 animal.getDeathDate() 如果动物还活着返回 null 是正确的做事方式。”那么,如果动物已经死了,但没人知道什么时候死呢?
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-12-08
              • 1970-01-01
              • 1970-01-01
              • 2015-02-12
              • 1970-01-01
              • 2016-04-19
              相关资源
              最近更新 更多