使用可变对象,我可以编写类似
的内容var user = DAO.getUser(id)
user.name = "John"
user.email ="john@doe.com"
// logic on user
如果用户是不可变的,那么我需要在每次更改操作时克隆\复制它。
我知道几种方法来执行此操作
最佳做法是什么?
还有一个问题。是否存在相对于原始对象获得“更改”的现有技术(例如,生成更新语句)?
答案 0 :(得分:9)
您提到的两种方式分别属于功能和OO范例。如果您更喜欢使用抽象数据类型进行功能分解,那么在Scala中,它由case类表示,然后选择copy方法。在我的选项中使用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
}
(不是最好的命令式代码,可以更短,但更清晰)。
原因是mutator有另一种方法,只是在每次调用时返回一个新对象:
class Person(val name: String,
val age: String) {
def changeName(newName: String): Unit = new Person(newName, age)
// ... and so on
}
但仍然是案例类方式更加明确。
如果你继续进行并发/并行编程,你会发现具有不可变值的功能概念要好得多,然后猜测你的对象当前处于什么状态。
<强>更新强>
感谢 senia ,忘了提两件事。
<强>镜头强>
在最基本的层面上,镜头是不可变数据的getter和setter,如下所示:
case class Lens[A,B](get: A => B, set: (A,B) => A) {
def apply(a: A) = get(a)
// ...
}
就是这样。镜头是一个包含两个功能的对象:get和set。 get接受A并返回B.set接受A和B并返回一个新的A.很容易看出类型B是A中包含的值。当我们传递一个实例时,我们返回该值。当我们传递A和B来设置时,我们更新A中的值B并返回反映该变化的新A.为方便起见,get是别名适用。 Scalaz Lens案例类有一个很好的intro
<强>记录强>
这个,因为来自shapeless库并称为记录。可扩展记录的实现,建模为关联的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)