【问题标题】:How to design an immutable value class holding java.lang.String?如何设计一个包含 java.lang.String 的不可变值类?
【发布时间】:2012-09-18 09:12:20
【问题描述】:

目标

创建一个类以用作 String 对象的不可变列表。

方法

我决定利用 Google GuavaImmutableList<E> 集合,而不是用 包装一个简单的 List<E> Collections.unmodifiableList(List<? extends T> list) 因为我知道这可以避免对不知道被包装的支持 List<E> 进行不必要的并发检查(来源:ImmutableCollectionsExplained)。

要求

  • 类是跨线程使用的“值持有者”
  • 不应允许任何代码在创建后更改内部值

锦上添花

  • 该类应实现 Iterable<E> 以按创建顺序迭代值
  • 对于给定的一组 String s,应该只有一个类。

尝试

这里有一些尝试,虽然更多的组合是可能的。原谅幽默的演绎。

尝试 #1(包括使用示例)

import java.util.List;
import com.google.common.collect.ImmutableList;
class BritnetSpearsSpellings implements Iterable<String> {
  public static BritnetSpearsSpellings of(String... spellings) {
    BritnetSpearsSpellings britneySpears = new BritnetSpearsSpellings();
    britneySpears.spellings = ImmutableList.copyOf(spellings);
    return britneySpears;
  }
  private List<String> spellings;
  private BritnetSpearsSpellings() {
  }
  public List<String> getSpellings() {
    return spellings;
  }
}
@Override
public Iterator<String> iterator() {
  return spellings.iterator();
}
public class Usage {
  public static void main(String[] args) {
    for (String sepllin : BritnetSpearsSpellings.of("Brittany Spears", "Brittney Spears", "Britany Spears"))
      System.out.printf("You spel Britni like so: %s%n", sepllin);
    }
  }
}

尝试 #2

class BritnetSpearsSpellings implements Iterable<String> {
  public static BritnetSpearsSpellings of(String... spellings) {
    BritnetSpearsSpellings britneySpears = new BritnetSpearsSpellings();
    britneySpears.spellings = ImmutableList.copyOf(spellings);
    return britneySpears;
  }
  private ImmutableList<String> spellings;
  private BritnetSpearsSpellings() {
  }
  public ImmutableList<String> getSpellings() {
    return spellings;
  }
  @Override
  public Iterator<String> iterator() {
    return spellings.iterator();
  }
}

尝试 #3

class BritnetSpearsSpellings implements Iterable<String> {
  public static BritnetSpearsSpellings of(String... spellings) {
    BritnetSpearsSpellings britneySpears = new BritnetSpearsSpellings(ImmutableList.copyOf(spellings));
    return britneySpears;
  }
  private final ImmutableList<String> spellings;
  private BritnetSpearsSpellings(ImmutableList<String> spellings) {
    this.spellings = spellings;
  }
  public ImmutableList<String> getSpellings() {
    return spellings;
  }
  @Override
  public Iterator<String> iterator() {
    return spellings.iterator();
  }
}

差异总结

  • 1 在公共接口中保留 List<E>,在 JavaDoc 中记录不变性。

  • 2 将所有内容存储并公开为 Google GuavaImmutableList<E>

  • 3 以创建专门的构造函数为代价将内部引用保持为最终引用,这可能会使静态工厂方法初始化在没有其他初始化选项(实际上存在于真实类中)的情况下看起来很傻

问题

请帮助我在这些实现中进行选择,并说明您选择背后的原因。

我认为方法 2 的主要缺点是客户需要对专门的 Google Guava 类型有认识/可见性,而他们可能不应该这样做?

【问题讨论】:

  • 您的类BritnetSpearsSpellings 只不过是一个包含ImmutableList 的简单包装器。为什么你需要这门课?为什么不直接使用ImmutableList
  • 我个人认为 #2 很好,您希望使用该课程的任何人都能够弄清楚 ImmutableList 的含义。如果您担心他们可能没有类定义,您可以只返回 List 和 JavaDoc 它是不可变的。
  • @Jesper Wrappers 还不错。显然,他不需要它,但对于使用/阅读他的代码的人来说,它仍然可以使事情变得更容易或更明显。
  • 我需要上面简化示例中缺少的额外功能。我需要包装器。

标签: java guava immutability


【解决方案1】:

显然,在我看来,#3 是最佳选择。 final 强制并记录类(该字段)的不可变性,而不仅仅是 List 本身不可变这一事实。

您是否使用一些 javadoc 公开 List 或在 getter 中公开实际的 ImmutableList 是另一回事。我已经看到了返回ImmutableList 清楚地记录意图的意见,但是,您仍然将自己绑定到实现而不是接口,以实现低级别并且将来可能需要更改(即使不变性是“一般”好)。因此,对于您的应用程序而言,它更像是一种全球设计选择,而不仅仅是一个用例。如果您确实使用ImmutableList,我认为这对客户来说不是一个真正的问题,名称是明确的,并且可以通过您的IDE很容易看出它是List的实现,如果他们可以访问javadoc他们想要更多信息。谁知道呢,他们可能会喜欢它并开始使用它,以及 Guava 提供的所有其他好东西:-)

【讨论】:

    【解决方案2】:

    根据您的问题陈述(“给定的字符串集应该只有一个类”),听起来您真正想要的是Interner&lt;ImmutableSet&lt;String&gt;&gt;

    • Interner 是一种实用程序(使用 Interners.newStrongInterner() 构造一个),用于确保您只有一个对象实例和任何给定数据。
    • 如果您的集合是不可变的并且支持成员资格测试的任意顺序的集合,ImmutableSet 是正确的选择。

    或者,您可能想查看Cache&lt;ImmutableSet&lt;String&gt;&gt;(有关详细信息,请参阅Caches Explained)。

    【讨论】:

      【解决方案3】:

      我会使用Attempt #2 而不是Attempt #1,因为它记录了您总是返回 ImmutableList 实例。一般来说,选择尽可能具体的返回类型和尽可能通用的参数类型是有用的。但是对于这两种情况,都更喜欢接口而不是具体的类。

      嗯,ImmutableList 是一个抽象类,而不是一个接口。但由于它的性质,它的行为非常像一个界面。

      Attempt #3 当你的类成员引用spellings 不能在对象的生命周期内改变和/或你想让整个类本身不可变时才有意义。否则当 spellings 可以在对象生命周期内分配给另一个 ImmutableList 时,它就没有意义了。

      鉴于您示例中的用例,我倾向于说#3 是最佳选择。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-02-29
        • 2012-04-03
        • 2012-06-24
        • 1970-01-01
        • 1970-01-01
        • 2011-09-13
        • 1970-01-01
        相关资源
        最近更新 更多