3

我正在做一个漂亮的项目,我试图让我的数据库层可以在不同的配置文件之间轻松交换,以便在内存数据库上编写测试。这个问题的灵感来自这个问题,但它与 slick 本身没有任何关系。

我对依赖类型没有丰富的经验,在我的例子中,我有以下特征,我用它来从数据库中抽象出一些类型:

trait Types {
  type A <: SomeType
  type B <: SomeOtherType
  val bTag: ClassTag[B]
}

然后我有另一个特征,基本上是我的(人造)蛋糕图案的一部分:

trait BaseComponent {
  type ComponentTypes <: Types

  val a: Types#A
  implicit val bTag: ClassTag[Types#B]
}

然后我有一个组件的实际实现,如下所示:

trait DefaultTypes {
  type A = SomeConcreteType
  type B = SomeOtherConcreteType
  val bTag = implicitly[ClassTag[B]]
}

trait DefaultBaseComponent extends BaseComponent {
  type ComponentTypes = DefaultTypes
  val ct = new ComponentTypes {}

  implicit val bTag = ct.bTag
}

我需要该标签,因为稍后服务将需要它(在我的实际实现中,我使用这种类型来抽象不同数据库库抛出的不同类型的异常);我很确定有更好的方法来做我想做的事情。

如果我没有实例化ComponentTypes特征以获取标记,并且我在 DefaultBaseComponent 中移动隐式召唤代码,它将召唤 anull代替ClassTag. 我需要有一种方法来引用我正在使用的实际类型(不同的A以及B我在不同环境中拥有的类型),并且我需要在其他组件中执行它而不知道它们是哪些实际类型。

我的解决方案有效,编译并通过了我为它编写的所有测试,谁能帮助我让它变得更好?

谢谢!

4

1 回答 1

0

Default您的示例对于所有这些s 和s有点不清楚Component- 也许更具体的示例(例如 DatabaseService / MysqlDatabaseService)会更清楚?

你需要ClassTag在它是抽象的地方传递——只有当你有一个具体的类型时,你才能“召唤”一个。您可能希望将值及其标签的概念打包:

trait TaggedValue[A] {val a: A; val ct: ClassTag[A]}
object TaggedValue {
  def apply[A: ClassTag](a1: A) =
    new TaggedValue[A] {
      val a = a1
      val ct = implicitly[ClassTag[A]]
    }
}

但这只是一件方便的事情。您还可以将一些traits 转换为abstract classes,允许您使用[A: ClassTag]隐式传递标签,但显然这会影响您可以乘以继承的类。

如果您遇到null的 s 听起来像是 trait 初始化顺序问题,但如果没有更具体的错误消息,则很难提供帮助。您可以通过将一些vals替换为defs 或使用早期初始化程序来解决它。

于 2014-11-08T14:50:53.723 回答