不变性和共享参考 - 如何协调?

时间:2010-04-08 22:07:19

标签: scala immutability

考虑这个简化的应用程序域:

  • 刑事调查数据库
  • Person是参与调查的任何人
  • Report是调查的一部分信息
  • Report引用主要Person(调查主题)
  • A Report有第二相关的同谋(当然可能是其他调查或报告的主要内容)
  • 这些类具有用于将它们存储在数据库中的ID,因为它们的信息可能会随着时间的推移而变化(例如,我们可能会找到一个人的新别名,或者将一个感兴趣的人添加到报告中)

Domain http://yuml.me/13fc6da0

如果这些存储在某种数据库中,我希望使用不可变对象,似乎存在关于状态和引用的问题。

假设我更改了一些关于Person的元数据。由于我的Person对象是不可变的,我可能会有一些代码:

class Person(
    val id:UUID,
    val aliases:List[String],
    val reports:List[Report]) {

  def addAlias(name:String) = new Person(id,name :: aliases,reports)
}

因此,带有新别名的Person成为新对象,也是不可变的。如果Report引用该人,但别名在系统的其他位置更改,则我的Report现在引用“旧”人,即没有新别名的人。

同样,我可能会:

class Report(val id:UUID, val content:String) {
  /** Adding more info to our report */
  def updateContent(newContent:String) = new Report(id,newContent)
}

由于这些对象不知道谁引用了它们,因此我不清楚如何让所有“引用者”知道有一个新对象可用来代表最近的状态。

这可以通过让所有对象从中央数据存储“刷新”并且创建新的,更新的对象的所有操作存储到中央数据存储来完成,但这感觉就像是基础语言引用的简洁重新实现。即,使这些“次要可存储物体”变得可变是更清楚的。因此,如果我向Person添加别名,则所有引荐人都会看到新值而不做任何事情。

当我们想要避免可变性时,如何处理这种情况,或者这种情况是不可变性无效?

3 个答案:

答案 0 :(得分:10)

如果X指向Y,两者都是不可变的,并且Y更改(即用更新的副本替换它),那么你别无选择,只能替换X(因为它已经改变,因为新的X指向新的Y,而不是旧的。)

这在高度互联的数据结构中保持快速变得令人头痛。你有三种一般方法。

  • 一般忘记不变性。使链接可变。根据需要修复它们。确保你确实修复了它们,否则你可能会遇到内存泄漏(X指的是旧的Y,它指的是旧的X,它指的是较旧的Y等)。
  • 不要存储直接链接,而是存储您可以查找的ID代码(例如,键入哈希映射)。然后,您需要处理查找失败案例,但其他方面非常强大。当然,这比直接链接慢一点。
  • 改变整个世界。如果某些内容发生了变化,那么链接到它的所有内容也必须进行更改(并且在复杂的数据集中同时执行此操作很棘手,但理论上可行,或者至少可以隐藏它的可变方面,例如有很多惰性值)

哪个更好取决于您的查找和更新速度,我希望。

答案 1 :(得分:5)

我建议你阅读他们如何处理clojure和Akka中的问题。阅读Software transactional memory。我的一些想法......

不可变性不是为了它本身而存在的。不变性是抽象的。它本质上不“存在”。世界是可变的,世界永远在变化。因此,数据结构是可变的很自然 - 它们描述了在给定时刻的真实或模拟对象的状态。它看起来像OOP rulez。在概念层面,这种态度的问题是RAM中的对象!=真实对象 - 数据可能不准确,它带有延迟等

因此,如果满足最琐碎的要求,您可以使用一切可变的东西 - 人员,报告等实际问题将在以下情况下出现:

  1. 数据结构是从并发线程修改的
  2. 用户为相同的对象提供令人震惊的更改
  3. 用户提供无效数据,应该回滚
  4. 使用天真的可变模型,您很快就会得到不一致的数据和破碎系统。可变性容易出错,不可变性是不可能的。你需要的是世界的交易视图。在交易程序中看到不可变的世界。 STM管理以一致且线程安全的方式应用的更改。

答案 2 :(得分:3)

我认为你正在尝试对这个圈子进行调整。 Person是不可变的,Person上的Reports列表是Person的一部分,Reports的列表可以更改。

对于一个不可变的Person是否有可能引用一个可变的PersonRecord来保存Reports和Aliases之类的内容?