2014-01-22 29 views
0

我正在为我的web应用程序中的操作构建一个框架。基本思想是它可以离线工作,所以我需要一种方式来传递发生的操作,然后可以重新合并,重播等等。 ActionMeta是启用此行为的类,并且表示单个操作,类型S是操作的主题类型。以下是构成它的实际参数。斯卡拉,将值传递给任何集合

case class ActionMeta[S](
    val timestamp: Instant, 
    val syncKey: SyncKey, 

    val action: Action[S], 
    val subjectId: UUID, 
    val subjectClientId: UUID, 

    val originMeta: JsValue, 
    val actionArgs: JsValue, 
    val status: ActionStatus, 
    val syncStatus: ActionSyncStatus, 
    val subActions: List[(Option[Any], ActionMeta[Any])] 
) { 
} 

这和它的行为工作,我的规格为单行动和具有相同主题的动作堆栈,但现在我需要解决的最后一行,也就是子行动。关键问题是子行动往往会涉及不同的主题。操作本身被继承的对象从这些性状:

trait Action[S] { 
    val registryKey: String 
    ActionRegistry.register(registryKey, this) 

    def getSubjectIds(subject: S): (UUID, UUID) 
    def pullOriginMeta(subject: S): JsValue 

    def getSubjectRepresentation(id: UUID, clientId: UUID): S 
    def saveSubjectRepresentation(subject: S): S 
    def merge(args: JsValue, newArgs: JsValue): Option[JsValue] 
    def deleteSubjectRepresentation(id: UUID, clientId: UUID): Boolean 
} 

trait CreationAction[S] extends Action[S] { 
    def apply(actionArgs: JsValue = JsNull): (S, ActionMeta[S]) = { 
    val (res, updatedActionArgs) = this.forwards(actionArgs) 
    val (sid, scid) = this.getSubjectIds(res) 
    val actionMeta = new ActionMeta[S](
     DateTime.now.toInstant, new SyncKey(), this, sid, scid, 
     JsNull, updatedActionArgs, Done, LocalAction, this.runSubActions(actionArgs) 
    ) 
    (res, actionMeta) 
    } 

    def forwards(args: JsValue): (S, JsValue) 
    def backwards(subject: S, args: JsValue): JsValue 

    def runSubActions(forwardArgs: JsValue): List[(Option[Any], ActionMeta[Any])] = { 
    List() 
    } 
} 

也有TransformAction [S]和DeletionAction [S]的性状,这是类似的,但具有不同类型的签名forwards/backwards,并为不同的逻辑apply

object TestSideActionCreate extends {val registryKey = "actions:testSideCreate"} 
    with TestSideActionBase with CreationAction[TestSide] { 

    // omitted method bodies, not necessary to problem 
    def forwards(args: JsValue): (TestSide, JsValue) = ??? 
    def backwards(subject: TestSide, args: JsValue): JsValue = ??? 
    def merge(args: JsValue, newArgs: JsValue): Option[JsValue] = ???  

    override def runSubActions(args: JsValue): List[(Option[Any], ActionMeta[Any])] = { 
    List(
     TestActionCreate(
     Json.obj("id" -> UUID.randomUUID.toString) 
    ).asInstanceOf[(Option[Any], ActionMeta[Any])] 
    ) 
    } 
} 

基本上我的问题是,runSubActions因为它是笨重的。这个方法可以工作,我可以像我期望的那样使用完整的类型来访问生成的子动作,但是,这仍然是我要做的很多我的最终实现,我的API,所以我不想如果我可以帮助它,则迫使编译器通过使用asInstanceOf来通过一系列不同类型的子动作。

有没有更好的方式来表达这种类型系统?我需要能够接受List[Option[Any], ActionMeta[Any]]中的任何内容,因为我不想把我的双手绑在一起:只要它实现了ActionMeta[S]并具有相应的Action[S],我可以依靠它来表现可预测性。

干杯。

回答

1

听起来就像你想要的HList。这是一个异类型列表,可以处理和保存各种类型的列表。因此,您可以将Int :: Double :: Foo :: HNil作为您的列表,并且该列表中的每个单独条目都会知道它的类型。斯卡拉最好,最强大,最完整的例子是Miles'Sabin Shapeless

也就是说,无形有它的权衡,也许一种方式来包装你的行动,隐藏或减轻需要S是为了更好地:

trait ActionOp[S]{ 
    def ids(that: S): (UUID, UUID) 
} 

trait Action{ 
    def getSubjectIds(that: S)(implicit ops: ActionOps[S]) = ops ids that 
} 

一些地方你传递的类型类型类也许?然后你可以重写你的代码来在类型类的“接口”上工作,而不用担心S的实际类型。

+0

包装听起来更符合我想要做的事情。想想在继续之前,我会再想一想如何去做。 – MalucoMarinero