2014-01-22 3 views
0

웹 응용 프로그램에서 작업에 대한 프레임 워크를 구축하고 있습니다. 기본 아이디어는 오프라인에서 작동한다는 것이므로 발생하는 동작을 전달하는 방법이 필요합니다. 그런 다음 되감기, 병합, 재생 등을 수행 할 수 있습니다. 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() 
    } 
} 

도 비슷하지만 forwards/backwards에 대해 서로 다른 유형의 서명이 TransformAction [S]와 DeletionAction [S] 특성, 그리고 다른 논리가있다 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입니다. 당신이 유형을 전달하는

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

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

뭔가 : 말했다

, 볼품이있다 무역 오프 및 숨기거나 S을 위해 더 좋을 것이다 필요성을 완화하기 위해 자신의 행동을 포장하는 어쩌면 방법입니다 아마도 타입 클래스일까요? 그런 다음 코드를 다시 작성하여 유형 클래스의 "인터페이스"에서 작업하고 실제 유형 인 S에 대해 걱정하지 않아도됩니다.

+0

줄 바꿈은 내가하고 싶은 것을 더 많이 들려줍니다. 계속하기 전에 내가 어떻게 할 것인지 오래 생각 해봐야 할 것 같아. – MalucoMarinero

관련 문제