不变性和共享引用 - 如何协调?
考虑这个简化的应用程序域:
- 刑事调查数据库
Person
是参与调查的任何人Report
是属于调查一部分的一些信息Report
> 引用主要Person
(调查的主题)Report
有次要相关的共犯(并且肯定可能是其他调查或报告中的主要对象)- 这些类的 id用于将它们存储在数据库中,因为它们的信息可能会随着时间的推移而改变(例如,我们可能会为某人找到新的别名,或将感兴趣的人添加到报告中)
如果这些存储在某种数据库中并且我希望使用不可变对象,则似乎存在有关状态和引用的问题假设
我更改有关 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)
}
: > 有了新的别名就成为一个新的对象,也是不可变的。如果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
添加别名,所有引用者都会看到新值,而不需要执行任何操作。
当我们想要避免可变性时,或者这是不变性没有帮助的情况时,如何处理?
Consider this simplified application domain:
- Criminal Investigative database
Person
is anyone involved in an investigationReport
is a bit of info that is part of an investigation- A
Report
references a primaryPerson
(the subject of an investigation) - A
Report
has accomplices who are secondarily related (and could certainly be primary in other investigations or reports - These classes have ids that are used to store them in a database, since their info can change over time (e.g. we might find new aliases for a person, or add persons of interest to a report)
Domain http://yuml.me/13fc6da0
If these are stored in some sort of database and I wish to use immutable objects, there seems to be an issue regarding state and referencing.
Supposing that I change some meta-data about a Person
. Since my Person
objects immutable, I might have some code like:
class Person(
val id:UUID,
val aliases:List[String],
val reports:List[Report]) {
def addAlias(name:String) = new Person(id,name :: aliases,reports)
}
So that my Person
with a new alias becomes a new object, also immutable. If a Report
refers to that person, but the alias was changed elsewhere in the system, my Report
now refers to the "old" person, i.e. the person without the new alias.
Similarly, I might have:
class Report(val id:UUID, val content:String) {
/** Adding more info to our report */
def updateContent(newContent:String) = new Report(id,newContent)
}
Since these objects don't know who refers to them, it's not clear to me how to let all the "referrers" know that there is a new object available representing the most recent state.
This could be done by having all objects "refresh" from a central data store and all operations that create new, updated, objects store to the central data store, but this feels like a cheesy reimplementation of the underlying language's referencing. i.e. it would be more clear to just make these "secondary storable objects" mutable. So, if I add an alias to a Person
, all referrers see the new value without doing anything.
How is this dealt with when we want to avoid mutability, or is this a case where immutability is not helpful?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果X引用Y,两者都是不可变的,并且Y发生变化(即用更新的副本替换它),那么您别无选择,只能也替换X(因为它已经更改,因为新的X指向新的Y,不是旧的)。
在高度互连的数据结构中,这很快就会成为维护的难题。您有三种一般方法。
我预计哪一个更可取取决于您的查找和更新速度。
If X refers to Y, both are immutable, and Y changes (i.e. you replace it with an updated copy), then you have no choice but to replace X also (because it has changed, since the new X points to the new Y, not the old one).
This rapidly becomes a headache to maintain in highly interconnected data structures. You have three general approaches.
Which is preferable depends on your rate of lookups and updates, I expect.
我建议您阅读 clojure 和 Akka 中人们如何处理该问题。了解软件事务内存。我的一些想法……
不变性的存在并不是为了它本身。不变性是抽象的。它本质上并不“存在”。世界是可变的,世界是永久变化的。因此,数据结构的可变性是很自然的——它们描述了真实或模拟对象在给定时刻的状态。这里看起来像 OOP 规则。在概念层面上,这种态度的问题是 RAM 中的对象!=真实对象 - 数据可能不准确,它会带来延迟等
因此,在最微不足道的要求的情况下,你可以使用所有可变的东西 - 人员,报告等实际问题将出现以下情况:
使用天真的可变模型,您很快就会得到不一致的数据并破坏系统。可变性容易出错,不变性则不可能。你需要的是事务性的世界观。在交易程序中看到的是不可变的世界。 STM 管理以一致且线程安全的方式应用的更改。
I suggest you to read how they people deal with the problem in clojure and Akka. Read about Software transactional memory. And some of my thoughts...
The immutability exists not for the sake of itself. Immutability is abstraction. It does not "exist" in nature. World is mutable, world is permanently changing. So it's quite natural for data structures to be mutable - they describe the state of the real or simulated object at a given moment in time. And it looks like OOP rulez here. At conceptual level the problem with this attitude is that object in RAM != real object - the data can be inaccurate, it comes with delay etc
So in case of most trivial requirements you can go with everything mutable - persons, reports etc Practical problems will arise when:
With naive mutable model you will quickly end up with inconsistent data and crushing system. Mutability is error prone, immutability is impossible. What you need is transactional view of the world. Within transaction program sees immutable world. And STM manages changes to be applied in consistent and thread-safe way.
我认为你正试图化圆为方。人员是不可变的,人员的报告列表是人员的一部分,并且报告列表可以更改。
不可变的 Person 是否可以引用可变的 PersonRecord 来保存报告和别名等内容?
I think you are trying to square the circle. Person is immutable, the list of Reports on a Person is part of the Person, and the list of Reports can change.
Would it be possible for an immutable Person have a reference to a mutable PersonRecord that keeps things like Reports and Aliases?