考虑这个简化的应用程序域:
Person
是参与调查的任何人Report
是调查的一部分信息Report
引用主要Person
(调查主题)Report
有第二相关的同谋(当然可能是其他调查或报告的主要内容)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
添加别名,则所有引荐人都会看到新值而不做任何事情。
当我们想要避免可变性时,如何处理这种情况,或者这种情况是不可变性无效?
答案 0 :(得分:10)
如果X指向Y,两者都是不可变的,并且Y更改(即用更新的副本替换它),那么你别无选择,只能替换X(因为它已经改变,因为新的X指向新的Y,而不是旧的。)
这在高度互联的数据结构中保持快速变得令人头痛。你有三种一般方法。
哪个更好取决于您的查找和更新速度,我希望。
答案 1 :(得分:5)
我建议你阅读他们如何处理clojure和Akka中的问题。阅读Software transactional memory。我的一些想法......
不可变性不是为了它本身而存在的。不变性是抽象的。它本质上不“存在”。世界是可变的,世界永远在变化。因此,数据结构是可变的很自然 - 它们描述了在给定时刻的真实或模拟对象的状态。它看起来像OOP rulez。在概念层面,这种态度的问题是RAM中的对象!=真实对象 - 数据可能不准确,它带有延迟等
因此,如果满足最琐碎的要求,您可以使用一切可变的东西 - 人员,报告等实际问题将在以下情况下出现:
使用天真的可变模型,您很快就会得到不一致的数据和破碎系统。可变性容易出错,不可变性是不可能的。你需要的是世界的交易视图。在交易程序中看到不可变的世界。 STM管理以一致且线程安全的方式应用的更改。
答案 2 :(得分:3)
我认为你正在尝试对这个圈子进行调整。 Person是不可变的,Person上的Reports列表是Person的一部分,Reports的列表可以更改。
对于一个不可变的Person是否有可能引用一个可变的PersonRecord来保存Reports和Aliases之类的内容?