不变性和共享引用 - 如何协调?

发布于 2024-08-28 13:17:58 字数 1337 浏览 7 评论 0原文

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

  • 刑事调查数据库
  • Person 是参与调查的任何人
  • Report 是属于调查一部分的一些信息
  • Report > 引用主要Person(调查的主题)
  • Report 有次要相关的共犯(并且肯定可能是其他调查或报告中的主要对象)
  • 这些类的 id用于将它们存储在数据库中,因为它们的信息可能会随着时间的推移而改变(例如,我们可能会为某人找到新的别名,或将感兴趣的人添加到报告中)

域 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)
}

: > 有了新的别名就成为一个新的对象,也是不可变的。如果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 investigation
  • Report is a bit of info that is part of an investigation
  • A Report references a primary Person (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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

ペ泪落弦音 2024-09-04 13:17:58

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

在高度互连的数据结构中,这很快就会成为维护的难题。您有三种一般方法。

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

我预计哪一个更可取取决于您的查找和更新速度。

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.

  • Forget immutability in general. Make the links mutable. Fix them as needed. Be sure you really do fix them, or you might get a memory leak (X refers to old Y, which refers to old X, which refers to older Y, etc.).
  • Don't store direct links, but rather ID codes that you can look up (e.g. a key into a hash map). You then need to handle the lookup failure case, but otherwise things are pretty robust. This is a little slower than the direct link, of course.
  • Change the entire world. If something is changed, everything that links to it must also be changed (and performing this operation simultaneously across a complex data set is tricky, but theoretically possible, or at least the mutable aspects of it can be hidden e.g. with lots of lazy vals).

Which is preferable depends on your rate of lookups and updates, I expect.

伊面 2024-09-04 13:17:58

我建议您阅读 clojure 和 Akka 中人们如何处理该问题。了解软件事务内存。我的一些想法……

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

因此,在最微不足道的要求的情况下,你可以使用所有可变的东西 - 人员,报告等实际问题将出现以下情况:

  1. 并发线程修改数据结构
  2. 用户对同一对象提供冲突的更改
  3. 用户提供无效数据,应该回滚

使用天真的可变模型,您很快就会得到不一致的数据并破坏系统。可变性容易出错,不变性则不可能。你需要的是事务性的世界观。在交易程序中看到的是不可变的世界。 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:

  1. data structures are modified from concurrent threads
  2. users provide conficting changes for the same objects
  3. a user provide an invalid data and it should be rolled back

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.

醉城メ夜风 2024-09-04 13:17:58

我认为你正试图化圆为方。人员是不可变的,人员的报告列表是人员的一部分,并且报告列表可以更改。

不可变的 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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文