2013-04-06 4 views
5

コラボレーションアクターのシステムに暗黙的に要求コンテキストを伝搬したいと思います。アクターシステムで暗黙的に要求コンテキストを渡す

私のシステムには複数のアクターがあり、これらのアクターに渡されるメッセージにはこのRequestContextオブジェクトが含まれている必要があります。

ActorAはMessageAの処理の一部として、それがビジネスロジックを実行し、その後からMessageBを構築ActorAがActorBにメッセージを送信する必要がある場合、型MessageB

のメッセージを受信するタイプMessageA ActorBのメッセージを受信しますロジックの結果と同様にMessageAで利用可能RequestContextの、その後、私たちは、処理されるメッセージのスルーを持っている、と明示的にRequestContextの周りを渡すと面倒です

def handle(ma:MessageA) { 
val intermediateResult = businessLogic(ma) 
actorB ! MessageB(intermediateResult, ma.requestContext) 
} 

をActorBに送信します。

私は、受信メッセージに埋め込まれたRequestContextを送信メッセージに明示的に挿入するのを避けるために、Scalaのimplicits機能を使用する独創的な方法を試しています。

メッセージは大文字と小文字の区別があります。私は暗黙のルールについて読んだことがあるが、オブジェクトの属性を現在の暗黙のスコープに持っていくことは遠くに見える。

これは、共通の要件であるはずです。 提案がありますか?

ありがとうございました。

trait RequestContext 

case class MessageA(req: RequestA, ctx: RequestContext) 
object MessageA { 
    def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx) 
} 

case class MessageB(req: RequestB, ctx: RequestContext) 
object MessageB { 
    def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx) 
} 

class Example extends Actor { 

    def receive = { 
    case MessageA(req, ctx) => handle(req)(ctx) 
    } 

    def handle(req: RequestA)(implicit ctx: RequestContext): Unit = { 
    val intermediateResult = businessLogic(req) // could take implicit ctx as well 
    actorB ! MessageB(intermediateResult) 
    } 
} 

しかし、宣言するときに、いくつかのオーバーヘッド依然として存在しているあなたが見ることができるように:このストレートフォワードを行い、問題のメッセージをお取り扱いが既に法に因数分解されて、あなたの例では、

答えて

6

私の意見では、最も簡単な方法は、ケースクラスであなたのvalは暗黙的にすることです。

case class MessageA(req: RequestA)(implicit val ctx: RequestContext) 

case class MessageB(req: RequestB)(implicit val ctx: RequestContext) 

def businessLogic(req:RequestA):RequestB 


def handle(ma: MessageA): Unit = { 
    // import all the members of ma so that there is a legal implicit RequestContext in scope 
    import ma._ 
    val intermediateResult = businessLogic(req) 
    actorB ! MessageB(intermediateResult) 
} 
+0

答えをありがとう。私は真剣にこのアプローチを検討しましたが、パラメータを明示的に渡すことは、機能を使用するためにすべてのインポートとインプリケートを追加するよりもはるかに読みやすく、あまり冗長ではないと考えました。 – vishr

+0

それが問題であれば、MessageA => MessageBというメッセージプロセッサを除外し、それをアクタ本体で呼び出すだけです。これはまた、テスト容易性を高めるだろう – Edmondo1984

6

メッセージタイプ、およびメソッドの署名も変更する必要があります。このスキームが価値があるかどうかは、これらの暗黙的な値の消費者とプロデューサの間の比率によって決まります(つまり、handleの複数のものが文脈を使用する場合は意味があります)。

上記の変化が考えられます。

case class MessageA(req: RequestA, ctx: RequestContext) 
object MessageA { 
    def apply(req: RequestA)(implicit ctx: RequestContext) = MessageA(req, ctx) 
    implicit def toContext(implicit msg: MessageA) = msg.ctx 
} 

case class MessageB(req: RequestB, ctx: RequestContext) 
object MessageB { 
    def apply(req: RequestB)(implicit ctx: RequestContext) = MessageB(req, ctx) 
    implicit def toContext(implicit msg: MessageB) = msg.ctx 
} 

... 
def handle(implicit ma: MessageA): Unit = { 
    val intermediateResult = businessLogic(req) 
    actorB ! MessageB(intermediateResult) 
} 
+0

なぜ暗黙のメッセージですか?暗黙のはずの要求ではありませんか?私の答えを参照してください – Edmondo1984

+0

@ローランド、あなたのアイデアは、きちんとしたアプローチを促しました。 MessageAとMessageBがAbstractMessageから継承し、AbstractMessageのコンパニオンオブジェクト内のRequestContextへの暗黙的な変換を提供する場合、問題は解決されます。各メッセージが行う必要があるのは、暗黙的な要求コンテキストパラメータを宣言することだけであり、利用可能な場合にはそれに沿って渡されます。 – vishr

+0

まあ...私は興奮しています...それは、暗黙のMessageAパラメータですべてのハンドルメソッドをマークすることは、これが動作するためには必要ですが、読みやすくすることはできません。暗黙の宣言は、ビジネスロジックコードに対してあまりにも多く伝播します。 – vishr

0

元のメッセージとコンテキストの両方を保持する汎用エンベロープクラスを作成します。 Actor(あなたはそれをakka._パッケージに入れなければなりません)に延長する形質を作ります。オーバーレイメソッドaroundReceive()は、エンベロープを展開し、現在のコンテキストを格納するアクタの保護された変数を初期化し、元の受信メソッドをアンパックされたメッセージで呼び出し、コンテキスト変数を初期化しません。むしろ疑わしいアプローチですが、これはまさにActor.sender()の動作です。

このようなコンテクストバウンドアクターにメッセージを送信するには、上記の封筒にメッセージを手動でラップするか、ActorRef以上の拡張機能を導入してこのタスクを自動化する必要があります。メッセージをコンテキスト・バインドに持ち上げる。

これは単なる概念ではなく、私が最近成功したアプローチの簡単な要約です。さらに、アクターベース、ThreadLocalベース、Futureベースなどの暗黙のコンテキスト環境という別の抽象的な概念を導入することでもう少し一般化しました。これにより、暗黙的にすべての同期/非同期操作チェーンでコンテキストデータを簡単に渡すことができました。

関連する問題