Java:如何处理“几乎”不可变的数据结构?

时间:2011-02-20 04:42:28

标签: java concurrency immutability builder

不可变对象很棒,因为它们在多线程应用程序中不需要特别小心或提供。然而,许多物体不能自然地变化。例如,一旦订单被填写,就会提交,处理并分配一个永久ID的订单。创建和提交订单时无法提供ID,但稍后(或可能永远不会)到达。

可能的解决方案:

  1. 分配一个额外的唯一ID 订单创建的时间。然后, 当永久(订单填写)ID 来吧,将它存储在地图中。所以 订单类将是不可变的。 然后,如果Map键不存在, 我们知道订单没有填写 然而。 (Map应该是静态类字段吗?)
  2. 另一种解决方案是制作 永久ID字段可变适用 适当的同步。 此外,我们可以限制ID 在对象中只设置一次 寿命。
  3. 这些解决方案是否合理?还有其他想法吗?感谢。

5 个答案:

答案 0 :(得分:9)

使类不可变,并使null成为ID的有效值。当您有要分配的ID时,将现有的不可变对象替换为相同的新对象,除了它具有新ID而不是旧ID。我喜欢使用名为withX的方法来实现这个目的。

Foo foo = new Foo("bar");
...
foo = foo.withId(12345); // replace foo with new derived object

答案 1 :(得分:3)

他们听起来像两个不同的对象订单填充订单。一个是另一个带有id的副本......

你感兴趣的不变性是什么?它会如何影响您的域模型?例如,我看到“域”对象有一个id字段只是为了取悦它,但是在hibernate给它们之前它们不会有id值。在这种情况下,“域”对象很弱。

业务视角(在我的学术示例中)的对象模型表明不需要id。但是,从技术角度的模型需要一个id(更具体地说,hibernate需要一个)。这里有明显的紧张,所以我想清楚我想要建模(业务或技术)。

出于兴趣,您的示例中的id代表什么?

因此,当我们考虑identity(在Eric Evans意义上)的想法时,对于一个对象或Entity存在,它必须具有一个身份(Entities是相等的如果他们的身份相等,无论他们的内容是否相等)。对我来说,这意味着

  

在没有身份的情况下新建Entity没有意义(在这种情况下为id

我也建议

  

没有“几乎”不可变的对象,它是不可变的或者不是

如果你使用上面建议的解决方法,你应该预先知道你的对象不再是不可变的。这可能没问题(再次,为什么你想要不变性?)。我不认为拥有一个可以为id设置null的域对象(稍后在副本中替换)是个好主意。它构成了一个“特殊情况”,可以通过不同的方式对其进行建模,并使您能够在很多地方(可能)处理特殊情况。

答案 2 :(得分:2)

我认为第二种方法是最好的,使用适当的同步。延迟初始化和同步对象getter的开销可能是微不足道的。

我还怀疑,如果比较完整的实现,第一种方法将具有相同(或更糟)的开销。例如,它要么需要同步Map,要么使用具有自己开销的并发映射。而且地图是共享数据结构这一事实意味着争用的可能性将高于(通常)非共享对象上的getter。

答案 3 :(得分:1)

定义ID-holder类,其中包含ID的字段。创建订单时,创建一个具有空白ID的新ID持有者对象并将其分配给订单。订单本身在技术上是浅不可变的,但是没有尝试为不同的订单“重复使用”ID持有者对象,ID持有者对象可能发生的唯一变化将是那些应该应用于订单或其任何副本。

答案 4 :(得分:-1)

遵循以下准则使类不可变:

a)确保不能覆盖类 - 使类最终,或使用静态工厂并使构造函数保持私有

b)将字段设为私有和最终

c)强制调用者在一个步骤中完全构造一个对象,而不是使用无参数构造函数和后续调用setXXX方法(即避免Java Beans约定)

d)不提供任何可以以任何方式改变对象状态的方法 - 不仅仅是setXXX方法,还有任何可以改变状态的方法

e)如果类有任何可变对象字段,那么在类和调用者之间传递时必须对它们进行防御性复制

参考链接:http://www.javapractices.com/topic/TopicAction.do?Id=29