可以将类型分组以传递给泛型类吗?

时间:2015-01-12 20:38:56

标签: scala

我正在构建一个存储库,我希望在多个实现中拥有一致的接口。这个存储库需要三个部分,因此它知道该做什么:Id,模型和事件。我将这些定义为特征,然后将它们组合为一个协议。

trait AbstractId
trait AbstractModel
trait AbstractEvent
abstract class Protocol 

以下是几个协议:

object BusinessProtocol extends Protocol {
  final case class Id(id: Int) extends AbstractId
  final case class Model(id: Id, name: String) extends AbstractModel

  sealed abstract class Event extends AbstractEvent
  final case class Create(id: Id, name: String) extends Event
}

object Order {
  final case class Id(id: Uid) extends AbstractId
  final case class Model(id: Id, cost: BigDecimal) extends AbstractModel

  sealed abstract class Event extends AbstractEvent
  final case class Create(id: Id, cost: BigDecimal) extends Event
  final case class Close(id: Id) extends Event
}

现在我定义了我的Repository界面。它必须分别接受模型,事件和Id。

trait Repository[M <: AbstractModel, E <: AbstractEvent, I <: AbstractId] {
  def hydrate(id: I): M
  def persist(id: I, event: E)
}

对于completness,这是实现的Repository的样子:

object BusinessRepo extends Repository[BusinessProtocol.Model, BusinessProtocol.Event, BusinessProtocol.Id] {
  override def hydrate(id: Id): Model = ???

  override def persist(id: Id, event: BusinessProtocol.Event): Unit = ???
}

这一切都有效,但我想有办法强迫两件事:

1)Make Protocols提供Id,Model和Event的定义。

2)使存储库只需要一个协议,并能够从协议中提取Id,模型和事件。类似的东西:

trait Repository[P <: Protocol] {
  def hydrate(id: P.Id): P.Model
  def persist(id: P.Id, event: P.Event)
}

这样,我可以强制Repository实现始终处理相关的三种类型。这可能吗?

1 个答案:

答案 0 :(得分:3)

让我们尝试一些类型级编程:

 trait Protocol {
   type E <: AbstractEvent    // define and Event  that extends AbstractEvent
   type I <: AbstractId          //....
   type M <: AbstractModel
} 

由此实现的是:

 object  BusinessProtocol {
   final case class Id(id: Int) extends AbstractId
   final case class Model(id: Id, name: String) extends AbstractModel
   sealed abstract class Event extends AbstractEvent
   final case class Create(id: Id, name: String) extends Event
 }

 class  BusinessProtocol extends Protocol{  
   import BusinessProtocol._
   //here they're assigned to the type variables
   type E = Event
   type I = Id
   type M = Model
}

然后是回购:

trait Repository { 
   type P <: Protocol
   def hydrate(id: P#I): P#M
   def persist(id: P#I, event : P#E)
}

BusinessRepo将是:

 class BusinessRepo extends Repository{
    type P = BusinessProtocol

    import BusinesProtocol._

    def hydrate(id: Id) : Model = {...}
    def persist(id:Id, event: Event) = {...}
}

您可以在this question

上找到背后的科学知识

编辑:  我试验了一点,你也可以这样做Repo:

 trait Repository[P <: Protocol]{
     def hydrate(id: P#I): P#M
     def persist(id: P#I, event : P#E)
 } 

 class BusinessRepo extends Repository[BusinessProtocol]{
   ....
 }

同样的效果,但他的一个使Repository更明确的签名。