在某些情况下是否无法创建不可变的 object 图?
Is it in some scenarios impossible to create immutable object graphs?
我知道不变性并不总是圣杯。然而,由于我现在学习 Scala 已经有一段时间了,所以我总是首先尝试找到一个不可变的解决方案,尤其是当涉及到纯 "data objects" 时。
我目前正在寻找一种为给定场景创建不可变 object 图的方法,但我不确定这是否可行。
我只想创建一次图形,不需要在创建后进行更改。
想象一下以下场景:
- 只有一种类型:
Person
。
Person
objects 可以有两种类型的引用:
- Person 和潜在 children(也是
Person
类型)之间存在单向 1-n 关系。
- 此外,妻子有丈夫,反之亦然
第一个问题是,夫妻二人的关系是周期性的。因为设置引用导致新的 objects(由于不变性),最终配偶 A 指向配偶 B_old,而配偶 B 指向配偶 A_old。另一个帖子中有人说循环引用和不变性是矛盾的。我不认为这总是正确的,因为配偶 A 可以在自己的构造函数中创建配偶 B 并传递 this
- 但即使使用这种不舒服的方法,之后添加 children 引用也会改变 A 和 B再次。反过来 - 从 children 开始,然后连接配偶 - 导致类似的情况。
目前,我认为没有办法做到这一点。但也许我错了,有一些我不知道的模式或解决方法。如果不是,可变性是唯一的解决方案吗?
我可以想出几种创建不可变循环的技巧,包括但不限于:
- 私有可变class,从外部实际上是不可变的
- 反思
但我最喜欢的(它是真正的 scala-way)是仔细混合惰性求值和按名称的参数:
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]] = ...
我知道不变性并不总是圣杯。然而,由于我现在学习 Scala 已经有一段时间了,所以我总是首先尝试找到一个不可变的解决方案,尤其是当涉及到纯 "data objects" 时。 我目前正在寻找一种为给定场景创建不可变 object 图的方法,但我不确定这是否可行。
我只想创建一次图形,不需要在创建后进行更改。
想象一下以下场景:
- 只有一种类型:
Person
。 Person
objects 可以有两种类型的引用:- Person 和潜在 children(也是
Person
类型)之间存在单向 1-n 关系。 - 此外,妻子有丈夫,反之亦然
- Person 和潜在 children(也是
第一个问题是,夫妻二人的关系是周期性的。因为设置引用导致新的 objects(由于不变性),最终配偶 A 指向配偶 B_old,而配偶 B 指向配偶 A_old。另一个帖子中有人说循环引用和不变性是矛盾的。我不认为这总是正确的,因为配偶 A 可以在自己的构造函数中创建配偶 B 并传递 this
- 但即使使用这种不舒服的方法,之后添加 children 引用也会改变 A 和 B再次。反过来 - 从 children 开始,然后连接配偶 - 导致类似的情况。
目前,我认为没有办法做到这一点。但也许我错了,有一些我不知道的模式或解决方法。如果不是,可变性是唯一的解决方案吗?
我可以想出几种创建不可变循环的技巧,包括但不限于:
- 私有可变class,从外部实际上是不可变的
- 反思
但我最喜欢的(它是真正的 scala-way)是仔细混合惰性求值和按名称的参数:
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]] = ...