我正在构建一个存储库,我希望在多个实现中拥有一致的接口。这个存储库需要三个部分,因此它知道该做什么: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实现始终处理相关的三种类型。这可能吗?
答案 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
更明确的签名。