2014-01-22 35 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