是否有最佳实践在未初始化/无时为scala选项分配值?

时间:2013-04-30 23:14:01

标签: scala functional-programming variable-assignment

我有以下类,其属性为Option[T]

class User extends IdBaseEntity[UUID] {
  var id: Option[UUID] = None
  var name: Option[String] = None
  var createdOn: Option[Date] = None
}

在某些数据访问层中,如果在将对象持久保存到cassandra之前未设置这些属性,则需要分配这些属性。以下是createdOn属性的几种方法。这些都是最好的方法还是我应该做的更好?

示例1

entity.createdOn = Some(entity.createdOn.map(identity).getOrElse(new Date()))

示例2

entity.createdOn = entity.createdOn.orElse(Some(new Date()))

示例3

entity.createdOn = entity.createdOn match {
  case None => Some(new Date())
  case _ => entity.createdOn
}

示例4

entity.createdOn = entity.createdOn match {
  case None => Some(new Date())
  case Some(x) => Some(x)
}

示例5

entity.createdOn match {
  case None => entity.createdOn = Some(new Date())
  case _ =>;
}

3 个答案:

答案 0 :(得分:4)

Option上的匹配并不是真正的惯用语(恕我直言)。我更喜欢orElsegetOrElse。我个人会选择例子2。

我不确定这是否适合您的用例,但让User成为一个不可变的案例类更为惯用:

case class User(id: Option[UUID] = None, ...)

copy它,而不是就地更新字段:

val updatedEntity = entity.copy(name = user.name.orElse(Some("Chris")))

答案 1 :(得分:1)

我会考虑改变你的设计 - 有两个原因:

  • 看起来User类在初始化后应该是只读的,所以像case类或val而不是var这样的东西会捕获这个要求:

    案例类用户(id:UUID,name:String,createdOn:Date);

  • 看起来每个用户都需要设置id,name和createdOn属性,因此Option []不是建模的好方法。

我经常在旁边的只读类中设置一个Builder类 简化和解耦对象构造过程 对象代表什么 - 类似于 此

object User {
    class Builder {
       var id:UUID = UUID.randomUUID()
       def id( v:UUID ):this.type = {id =v; this; }

       var name:String = id.toString
       def name( v:String ):this.type = { name=v; this; }

       var createdOn:Date = new Date()
       def createdOn( v:Date ):this.type = { createdOn = v; this; }

       def build():User = {
         assert( Seq(id,name,createdOn).find( _ == null ).isEmpty, "Must set all props" )
         User( user, name, createdOn )
       }
   }
}

无论如何 - 这是另一种做事的方式......

答案 2 :(得分:1)

由于场景是“获取属性值并在某些条件成立时更新它”,我将尝试封装对属性的访问。例如:

/**
 * Read+write access to property `P` of object `R`.
 */
case class Accessor[R,P](get: R => P, set: (R, P) => Unit) {
  /** Utility for updating values. */
  def update(record: R, modfn: P => P) =
    set(record, modfn(get(record)));
}

class User {
  var id: Option[Int] = None;
}
object User {
  // For each property of `User` we need to define an accessor,
  // but this is one time job:
  val idA: Accessor[User,Option[Int]] =
      Accessor((u: User) => u.id,
               (u: User, r: Option[Int]) => u.id = r);
}

object Test {
  import User._;

  // We can do a lot of stuff now with accessors, for example set
  // default values for `Option[...]` ones:
  def setDefault[A,B](record: A,
                      accessor: Accessor[A,Option[B]],
                      defPropVal: => B) =
    accessor.update(record, _.orElse(Some(defPropVal)));

  val user = new User();
  // Set user's id, if not defined:
  setDefault(user, idA, 42);
}

因此,我们不是为每个属性定义一个特定的方法来填充默认值,而是为每个属性定义一个通用访问器。然后我们可以使用它们来实现所有其他的东西。