Scala“更新”不变的对象最佳实践 [英] Scala "update" immutable object best practices

查看:124
本文介绍了Scala“更新”不变的对象最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有了一个可变对象,我可以写一些类似于

  var user = DAO.getUser(id)
user .name =John
user.email =john@doe.com
//逻辑关于用户

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



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


  1. 案例类拷贝

  2. 方法(如changeName)用新属性创建一个新对象

最佳做法是什么?

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

解决方案

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

  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}
// ...等等
}

(不是最好的命令代码,can可能会更短,但清晰)。

原因是有另一种方式与增变器,只是为了在每次调用时返回一个新的对象:

  class Person(val name:String,
val age:String){
def changeName(newName:String):Unit = new Person新名称,年龄)
// ...等等
}

但仍然案例类的方式更多consise。



如果你进一步研究并发/并行编程,你会发现具有不可变值的功能概念要好得多,然后猜测你的对象当前处于什么状态。



更新



感谢 senia ,忘了提及两件事。

镜头
在最基本的层面上,镜片是一些吸气剂和二手烟对于不可变的数据,如下所示:

  case class Lens [A,B](get:A => B,集合:(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案例课程有一个很好的介绍

记录

这其中的一个因素来自无形的库和称为记录。可扩展记录的实现建模为关联的HList。键使用单例类型进行编码,并完全确定它们相应值的类型(例如github):

  object author extends Field [字符串] 
对象标题扩展字段[字符串]
对象价格扩展字段[双]
对象inPrint扩展字段[布尔]

val书=
(作者 - >Benjamin Pierce)::
(标题 - >类型和编程语言)::
(价格 - > 44.11)::
HNil

//读取价格字段
val currentPrice = book.get(price)//推断类型为Double
currentPrice == 44.11

//更新价格依赖于currentPrice的静态类型
val updated = book +(price - >(currentPrice + 2.0))

//添加一个新字段
val extended = updated +(inPrint - > true)


With a mutable object I can write something like

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

If user is immutable then I need to clone\copy it on every change operation.

I know a few ways to perform this

  1. case class copy
  2. method (like changeName) that creates a new object with the new property

What is the best practice?

And one more question. Is there any existing technique to get "changes" relative to the original object(for example to generate update statement)?

解决方案

Both ways you've mentioned belongs to functional and OO paradigms respectively. If you prefer functional decomposition with abstract data type, which, in Scala, is represented by case classes, then choose copy method. Using mutators is not a good practice in my option, cause that will pull you back to Java/C#/C++ way of life.

On the other hand making ADT case class like

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

is more consise then:

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

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

(not the best imperative code, can be shorter, but clear).

Of cause there is another way with mutators, just to return a new object on each call:

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

But still case class way is more consise.

And if you go futher, to concurrent/parallel programming, you'll see that functional consept with immutable value are much better, then tring to guess in what state your object currently are.

Update

Thanks to the senia, forgot to mention two things.

Lenses
At the most basic level, lenses are sort of getters and setters for immutable data and looks like this:

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

That is it. A lens is a an object that contains two functions: get and set. get takes an A and returns a B. set takes an A and B and returns a new A. It’s easy to see that the type B is a value contained in A. When we pass an instance to get we return that value. When we pass an A and a B to set we update the value B in A and return a new A reflecting the change. For convenience the get is aliased to apply. There is a good intro to Scalaz Lens case class

Records
This one, ofcause, comes from the shapeless library and called Records. An implementation of extensible records modelled as HLists of associations. Keys are encoded using singleton types and fully determine the types of their corresponding values (ex from 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)

这篇关于Scala“更新”不变的对象最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆