2

我喜欢不变性的概念。我也喜欢没有空值的概念(如果可能的话,也尽可能没有 NullDesignPattern,没有 NullObjects ......)。

但是下面的场景呢:

我有一个 object User,它有两个字段:birthdayand dateInLifeMarried(可以是任何类似的字段;重要的是,起初这个字段是null并且在对象生命中的某个时刻发生变化)。

由于它是不可变的,我希望这两个字段都在构造函数中:

public User(birthday, dateInLifeMarried)

现在:

  1. 我不想传递null给第二个参数
  2. 我不想添加一个 setter,因为它是不可变的
  3. 我不想用 NullObjectPattern 而不是调用构造函数null

我只是在自相矛盾,还是有一种我没有想到的优雅方式来拥有它?

4

4 回答 4

13

那么你需要考虑你想要的表示是什么 - 而不仅仅是构造函数签名。我怀疑您需要对该字段使用空引用(或至少类似的东西)。但是你可以有两个构造函数:

User(birthday)               // Unmarried user
User(birthday, marriageDate) // Married user; marriageDate must not be null

作为 API 的用户,我不确定我是否真的喜欢这样 - 我想我宁愿允许marriageDate为空。特别是,必须编写以下内容会很烦人:

LocalDate marriageDate = getMarriageDateOrNull();
User user = marriageDate == null ? new User(birthday)
                                 : new User(birthday, marriageDate);

或者,由于人们可以多次结婚,因此您始终可以使用Iterable<LocalDate> marriageDates,然后从未结婚的用户将有一个空序列:) (尽管您需要考虑已婚然后离婚和结婚然后丧偶的用户. 模拟现实生活很难。)

于 2013-02-20T09:47:36.093 回答
1

第二个构造函数呢

User(birthday)

调用第一个

this(birthday, null)

?

于 2013-02-20T09:49:41.120 回答
1

不要为了避免 null 而避免它。想想什么null意思,然后在有意义的地方使用它。该值null表示未知未定义或未指定的值。如果您有意识地使用 null,您可以使您的代码更易于使用和更具可读性,同时防止空指针异常。

在您的示例中,可以未指定结婚日期,因此您应该允许null. 但是,您不希望未指定生日,因此您不允许 null 存在。您可以像这样指定构造函数:

User(LocalDate birthday)
{
    this(birthday, null);
    // That's it! Nothing more to do here.
}

User(LocalDate birthday, LocalDate marriageDate)
{
    if (birthday == null)
        throw new IllegalArgumentException();
    // Use it...
}

现在您(或任何使用您的代码的人)可以执行以下操作:

LocalDate marriageDate = getMarriageDateOrNull();
User user = new User(birthday, marriageDate);

看?更简洁的代码,因为你授权 null而不是避免它。

于 2013-02-20T10:50:04.743 回答
0

您可以使用继承:

public abstract class User{}
public class UnMarriedUser extends User{}
public class MarriedUser extends User{}

但是这个模型在面对许多排列时变得越来越弱。

于 2013-02-20T09:52:43.763 回答