我们使用案例类来表示在客户端和服务器之间传输的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字段中使用'魔术'号码。
有没有人能更好地解决这个问题?
答案 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")