可以对类型进行分组以传递给泛型 class?
Possible to group types for passing to generic class?
我正在构建一个存储库,我希望它具有跨多个实现的一致接口。这个存储库需要三个部分以便它知道要做什么:一个 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)
}
为了完整起见,下面是已实现的存储库的样子:
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) 使协议提供 Id、模型和事件的定义。
2) 让存储库只接受一个协议,并能够从协议中提取 Id、模型和事件。类似于:
trait Repository[P <: Protocol] {
def hydrate(id: P.Id): P.Model
def persist(id: P.Id, event: P.Event)
}
这样,我可以强制 Repository 实现始终在相关的三种类型上运行。这可能吗?
让我们通过一些类型级编程来尝试一下:
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
}
然后是 Repo:
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
上找到这背后的科学
编辑:
我做了更多的实验,你也可以像这样做回购:
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
的签名更加明确。
我正在构建一个存储库,我希望它具有跨多个实现的一致接口。这个存储库需要三个部分以便它知道要做什么:一个 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)
}
为了完整起见,下面是已实现的存储库的样子:
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) 使协议提供 Id、模型和事件的定义。
2) 让存储库只接受一个协议,并能够从协议中提取 Id、模型和事件。类似于:
trait Repository[P <: Protocol] {
def hydrate(id: P.Id): P.Model
def persist(id: P.Id, event: P.Event)
}
这样,我可以强制 Repository 实现始终在相关的三种类型上运行。这可能吗?
让我们通过一些类型级编程来尝试一下:
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
}
然后是 Repo:
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
上找到这背后的科学编辑: 我做了更多的实验,你也可以像这样做回购:
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
的签名更加明确。