Scala 在某些情况下,是否不可能创建不变的对象图?

Scala 在某些情况下,是否不可能创建不变的对象图?,scala,functional-programming,immutability,object-graph,Scala,Functional Programming,Immutability,Object Graph,我知道不变性并不总是圣杯。然而,由于我学习Scala已经有一段时间了,所以我总是首先尝试找到一个不变的解决方案,特别是当涉及到纯“数据对象”时。 我目前正在寻找一种为给定场景创建不可变对象图的方法,但我不确定这是否可行 我只想创建一次图形,创建后的更改是不必要的 想象一下以下场景: 只有一种类型:Person Person对象可以有两种类型的引用: 一个人和潜在儿童之间存在单向1-n关系(也属于Person) 此外,妻子有丈夫,反之亦然 第一个问题是配偶之间的关系是循环的。因为设置引用会

我知道不变性并不总是圣杯。然而,由于我学习Scala已经有一段时间了,所以我总是首先尝试找到一个不变的解决方案,特别是当涉及到纯“数据对象”时。 我目前正在寻找一种为给定场景创建不可变对象图的方法,但我不确定这是否可行

我只想创建一次图形,创建后的更改是不必要的

想象一下以下场景:

  • 只有一种类型:
    Person
  • Person
    对象可以有两种类型的引用:
    • 一个人和潜在儿童之间存在单向1-n关系(也属于
      Person
    • 此外,妻子有丈夫,反之亦然
第一个问题是配偶之间的关系是循环的。因为设置引用会产生新对象(由于不变性),所以最终配偶A指向配偶B_old,配偶B指向配偶A_old。另一篇帖子中有人说循环引用和不变性是一个矛盾的说法。我不认为这总是正确的,因为配偶A可以在其自己的构造函数中创建配偶B并传递
this
——但即使使用这种不舒服的方法,在之后添加子引用也会再次更改A和B。另一种方式——从孩子开始,然后将配偶联系起来——也会导致类似的情况


目前,我认为没有办法做到这一点。但也许我错了,有些模式或变通方法我不知道。如果不是,可变性是唯一的解决方案吗?

我可以想象一些技巧如何创建不可变循环,包括但不限于:

  • 私有可变类,它实际上是从外部不可变的
  • 倒影
但我最喜欢的一个(这是真正的scala方式)是仔细混合的惰性计算和按名称参数:

object DeferredCycle extends App {

  class C(val name:String, _child: => C) {
    lazy val child = _child
    override def toString: String = name + "->" + child.name
  }

  val a:C = new C("A", b)
  val b:C = new C("B", a)

  println(a)
  println(b)
}
印刷品:

A->B
B->A

要添加另一个透视图,您不必总是将关系建模为包含。您可以添加另一级别的间接寻址,例如不透明标识符

case class PersonId(id: Int)
case class Person(id: PersonId, name: String, spouse: Option[PersonId], children: Seq[PersonId])

val people: Map[PersonId, Person] = ...
或者,关系甚至不需要是
Person
的成员,它们也可以在外部维护:

case class PersonId(id: Int)
case class Person(id: PersonId, name: String)

val people: Map[PersonId, Person] = ...
val spouses: Map[PersonId, PersonId] = ...
val children: Map[PersonId, Seq[PersonId]] = ...

谢谢,非常有用。只有一个问题:您在创建
a
(referenceing
b
)实例时所做的“正向引用”只起作用,因为
b
延迟周期
对象的属性,对吗?因此,如果
a
尤其是
b
只是方法内部的局部变量,那么这就不起作用了。还是我在这一点上错了?@DaniW,你是对的,但是你可以使用无数的技巧来克服这个限制。您可以在方法内部创建本地对象,您可以将这些类定义为
var
s(然后可以选择重新定义为
val
s),您可以将它们创建为某些集合的元素,等等。@Aivean我想知道“私有可变类”是什么样子。它是否有点像只能使用一次的可变setter,或者通过布尔标志控制?编辑:类似于
set(c:Child):Unit={if(switch)this.Child=c;switch=false}
?@ceran,您的方法也是可行的,但更接近于“验证不变量”。我所说的“私有可变类”实际上是指私有setter,它只能从创建实例的地方访问,比如一些工厂。当实例离开工厂时,可以认为它实际上是不可变的,因为setter是私有的。我在问题中没有说的是,
Person
有几个子类型。如果引用只由ID完成,我想我会失去相当多的类型安全性。我想更新你的帖子,但我需要先获得15学分:/