是否可以减少代码重复次数

时间:2017-04-13 21:19:15

标签: scala

我们使用案例类来表示在客户端和服务器之间传输的JSON对象。它一直很好用,除了我们已经存在了很长一段时间的一个关键点,我想知道是否有人有一个聪明的方法。

假设我有一个用户对象,其中包含id,名字,姓氏和电子邮件地址。一旦用户被保存到数据库,他就有一个id(Int)分配给他,因此对于处理现有用户的客户端和服务器之间的所有通信,id是必填字段。实际上,只有一种情况是不需要id字段,而且当用户第一次被保存时。我们目前处理此问题的方法是使用如下所示的案例类:

case class User(id: Option[Int], firstName: String, lastName: String, email:String)

除了初始保存之外的所有情况,该ID都是Some,初始保存ID始终为None,因此我们发现自己经常使用id.getOrElse(0)。 (有时候我们会做.get,但感觉很脏。)

我希望拥有的对象是现有用户的id: Int字段和新用户根本没有id字段的对象,但没有在两个单独的案例类中两次声明所有其他字段。但是,我没有找到方便的方法。我也不喜欢在新用户的id字段中使用'魔术'号码。

有没有人能更好地解决这个问题?

3 个答案:

答案 0 :(得分:1)

case class User[+IdOpt <: Option[Int]](idOpt: IdOpt, firstName: String, lastName: String, email:String)
object User {
  // Type aliases for convenience and code readability
  type New = User[None.type]
  type Saved = User[Some[Int]]
  type Value = User[Option[Int]] // New or Saved

  implicit class SavedOps(val user: Saved) extends AnyVal {
    def id: Int = user.idOpt.get
  }
}

试验:

scala> val billNew = User(None, "Bill", "Gate", "bill@microsoft.com")
billNew: User[None.type] = User(None,Bill,Gate,bill@microsoft.com)

scala> billNew.id 
<console>:17: error: value id is not a member of User[None.type]
       billNew.id
               ^

scala> val billSaved = billNew.copy(idOpt = Some(1))
billSaved: User[Some[Int]] = User(Some(1),Bill,Gate,bill@microsoft.com)

scala> billSaved.id
res1: Int = 1

答案 1 :(得分:1)

这就是我们现在最终的目标。

trait Resource[T <: Option[Int]] {
  def idOpt: T
}

object Resource {
  type IsSome = Some[Int]
  implicit class SomeOps[R <: Resource[IsSome]](val resource: R) {
    def id: Int = resource.idOpt.get
  }
}

这允许我们像这样使用它:

case class User[T <: Option[Int]](idOpt:T, firstName:String, lastName:String, email:String) extends Resource[T]
case class Company[T <: Option[Int]](idOpt:T, companyName: String) extends Resource[T]

val u1 = User(None, "Bubba", "Blue", "bubba@shrimp.com")
val u2 = User(Some(1), "Forrest", "Gump", "forrest@shrimp.com")
u1.id // <-- won't compile
u2.id // <-- compiles

答案 2 :(得分:0)

如果你把它隐藏起来,那么拥有一个神奇的数字并不是一个糟糕的主意。实际上它是一种常见的模式,Slick使用它作为例子。您可以忽略要插入的对象的id值。

所以你可以先把构造函数包私有化

 case class User private[db](id: Int, firstName: String, lastName: String, email:String)

然后为用户提供一个伴随对象,无需id

即可创建它
 object User{
   def apply(firstName: String, lastName: String, email: String): User = User(-1, firstName, lastName, email)
}

现在你可以构建它,好像不需要id

val user = User("first","last","email")