【问题标题】:Scala "update" immutable object best practicesScala“更新”不可变对象最佳实践
【发布时间】:2013-07-07 02:55:00
【问题描述】:

有了一个可变对象,我可以写出类似的东西

var user = DAO.getUser(id)
user.name = "John"
user.email ="john@doe.com"
// logic on user

如果用户是不可变的,那么我需要在每次更改操作时克隆\复制它。

我知道一些方法来执行此操作

  1. 案例类文案
  2. 使用新属性创建新对象的方法(如 changeName)

最佳做法是什么?

还有一个问题。是否有任何现有技术可以获取相对于原始对象的“更改”(例如生成更新语句)?

【问题讨论】:

  • 要获取更改,您可以使用事件溯源。

标签: scala functional-programming immutability scala-2.10


【解决方案1】:

您提到的这两种方式分别属于功能范式和 OO 范式。如果您更喜欢抽象数据类型的功能分解,在 Scala 中,它由案例类表示,然后选择复制方法。在我的选择中,使用 mutators 不是一个好习惯,因为这会让你回到 Java/C#/C++ 的生活方式。

另一方面,制作 ADT 案例类

case class Person(name: String, age: String)

这样更简洁:

class Person(_name: String, _age: String) {
  var name = _name
  var age = _a

  def changeName(newName: String): Unit = { name = newName }
  // ... and so on
}

(不是最好的命令式代码,可以更短,但清晰)。

当然,还有另一种使用 mutators 的方法,只是在每次调用时返回一个新对象:

class Person(val name: String, 
             val age: String) {      
  def changeName(newName: String): Unit = new Person(newName, age)
  // ... and so on
}

但还是 case class 方式更简洁。

如果您进一步了解并发/并行编程,您会发现具有不可变值的函数式概念要好得多,然后尝试猜测您的对象当前处于什么状态。

更新

感谢senia,忘了提两件事。

镜头
在最基本的层面上,镜头是不可变数据的 getter 和 setter,看起来像这样:

case class Lens[A,B](get: A => B, set: (A,B) => A) {
  def apply(a: A) = get(a)  
  // ...
}

就是这样。镜头是一个包含两个功能的对象:获取和设置。 get 接受一个 A 并返回一个 B。set 接受一个 A 和 B 并返回一个新的 A。很容易看出类型 B 是包含在 A 中的值。当我们传递一个实例来获取时,我们返回该值。当我们传递一个 A 和一个 B 来设置时,我们会更新 A 中的值 B 并返回一个反映更改的新 A。为方便起见,get 被别名应用。 Scalaz Lens 案例类有一个很好的intro

记录
这个,ofcause,来自shapeless库,叫做Records。建模为关联 HLists 的可扩展记录的实现。键使用单例类型进行编码,并完全确定其对应值的类型(例如来自 github):

object author  extends Field[String]
object title   extends Field[String]
object price   extends Field[Double]
object inPrint extends Field[Boolean]

val book =
  (author -> "Benjamin Pierce") ::
  (title  -> "Types and Programming Languages") ::
  (price  ->  44.11) ::
  HNil

// Read price field
val currentPrice = book.get(price)  // Inferred type is Double
currentPrice == 44.11

// Update price field, relying on static type of currentPrice
val updated = book + (price -> (currentPrice+2.0))

// Add a new field
val extended = updated + (inPrint -> true)

【讨论】:

  • 您可以在答案中添加第三种方式:lenses。新答案还不够,但值得一提。
猜你喜欢
  • 2021-07-31
  • 1970-01-01
  • 1970-01-01
  • 2012-07-31
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 2014-11-18
相关资源
最近更新 更多