【问题标题】:Case class inheritance is prohibited, but how to model dependency from library?禁止案例类继承,但如何从库中建模依赖?
【发布时间】:2017-04-13 09:07:10
【问题描述】:

我有三个不同的 Scala 项目,其中一个只包含用于其他两个 Scala 项目(以下称为库)的模型。这三个项目之一是 Play Web 应用程序,我想将模型库中的模型保存在 MongoDB 中(代码位于 Play 应用程序中)。

所以在我的库中,我有以下模型:

case class MyUser(id: UUID = UUID.randomUUID(), timestamp: Instant = Instant.now, name: String)

要在我的 Play 应用中持久化 MyUser 的实例,我通常会使用这个模型:

case class MyUser(var _id: Option[BSONObjectID] = None,
                   id: UUID,
                   timestamp: Instant,
                   name: String,
                   var created: Option[DateTime] = None,
                   var updated: Option[DateTime] = None
                  ) extends Temporal

现在我正在寻找一种方法将我的库的MyUser 变形为我的 Play 应用程序中的相应类,所以我想我会做这样的事情:

case class MyUser(var _id: Option[BSONObjectID] = None,
                   var created: Option[DateTime] = None,
                   var updated: Option[DateTime] = None
                  ) extends lib.MyUser with Temporal

但是,现在我收到以下错误:

禁止逐个继承。要克服此限制,请使用提取器在非叶节点上进行模式匹配。

我想过做这样的事情:

case class MyUser(var _id: Option[BSONObjectID] = None,
                   user: lib.MyUser,
                   var created: Option[DateTime] = None,
                   var updated: Option[DateTime] = None
                  ) extends Temporal

我怎样才能以正确的方式设计这个?

【问题讨论】:

  • 您愿意放弃lib.MyUser 上的case class 构造吗?
  • 好吧,我宁愿保留case class 构造。从设计的角度来看,什么会更好:向 MyUser 添加一个新字段,该字段包含 lib.MyUser 并用作包装器或继承?

标签: scala playframework playframework-2.0


【解决方案1】:

首先,错误告诉您的是,您不应该为扩展的类使用case class。它所说的提取器基本上是,如果需要在父类上进行模式匹配(这是 case classcase 的来源,简单的模式匹配),您总是可以为它编写自定义提取器。

但是,这不适用于您的情况,因为您无法更改您的 lib 公开 case classes 的事实。

另一种解决方案是考虑两个类之间没有继承。实际上,一个是用户的表示,而另一个是包含用户的存储单元的表示。所以看起来你提出的解决方案毕竟有一些意义。

不过,必须在序列化对象(在 Mongo 中)和 JVM 类中添加一个字段似乎很麻烦,您需要始终执行 mongoUser.user 从 @ 获取 lib.MyUser 987654327@...

一个优雅的(恕我直言)解决方案是保留此字段 user: lib.MyUser,但要让您的序列化将此字段展平到 BSON 对象的顶层,以便序列化的 MyUser 看起来像

{
  _id: ObjectId(_),
  id: 123456789abc-1234-1234-1234,
  ...
}

就好像你继承了lib.MyUser 的字段一样。

反序列化

现在,每当您想从 Mongo 获取 lib.MyUser 时,仅用于读取目的,您可以直接以这种格式反序列化它,忽略添加的字段。但是,如果您需要对其进行一些更新,则必须将其反序列化为 MyUser,以便能够更新此特定的 BSON 文档。

scala 用法

当您将对象反序列化为MyUser(例如,用于更新)时,您可能仍希望轻松访问lib.MyUser 中公开的所有字段,而不必每次都访问字段user。这可以通过隐式转换来完成。

概括

顺便说一句,你可以对任何你想要序列化的对象执行此操作,以通用方式...

总结一下

case class MongoRepr[T](_id: Option[BSONObjectId]
                         value: T,
                         created: Option[DateTime],                             
                         updated: Option[DateTime])

object MongoRepr {
  //implement flattened (de)serialization, this depends on your
  // mongo driver and what you're using for serialization (JSON/BSON)
  def handler[T](implicit handlerT: BSONHandler[T]): BSONHandler[MongoRepr[T]] = ???

 implicit def toValue[T](repr: MongoRepr[T]): T = repr.value
}

【讨论】:

    猜你喜欢
    • 2014-12-31
    • 1970-01-01
    • 2021-02-12
    • 2015-06-23
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多