1

ソケットを介して接続されたクライアントサーバーアプリケーションがあります。私のMessageHandlerクラスは、着信メッセージと発信メッセージの処理を担当します。リクエストとコールバックを満たすために必要な引数を渡します。これらは、レスポンスが受信された後に呼び出されます。コールバックを一意のリクエスト識別子を使用してハッシュマップに保存します。応答を受信すると、ハッシュマップからコールバックを取得し、それを呼び出して、応答を引数として渡します。これがコードです

class MessageHanlder {
  val callbacks = new HashMap[String, (AnyRef) => Unit]

  def sendAuthRequest(login: String, password: String, callback: Option[(AnyRef) => Unit]) {
    val requestId = generateRequestId()
    // create a packet with requestId, login and password
    // send the packet
    if(callback.isDefined) callbacks += ((requestId, callback.get))
  }

  private def generateRequestId() = // returns random string

  def handleAuthResponse(authResponse: AuthResponse) {
    val requestId = authResponse.requestId
    val callbackOption = callbacks.get(requestId)
    if(callbackOption.isDefined) callbackOption.get(authResponse)
  }

  def sendServerInfoRequest(callback: Option[(AnyRef) => Unit]) {
    val requestId = generateRequestId()
    // create a packet with requestId
    // send the packet
    if(callback.isDefined) callbacks += ((requestId, callback.get))
  }

  def handleServerInfoResponse(serverInfoResponse: ServerInfoResponse) {
    val requestId = serverInfoResponse.requestId
    val callbackOption = callbacks.get(requestId)
    if(callbackOption.isDefined) callbackOption.get(serverInfoResponse)
  }

私の問題は、コールバックの引数タイプです。ServerInfoResponse、AuthResponse、またはその他の応答タイプにすることができます。各応答には、コールバックからアクセスしたい独自のフィールドのセットがあります。コールバックをハッシュマップに保存するには、引数の型をAnyRefとして一般化する必要があります。したがって、コールバックでは、AnyRefを次のような具象型にキャストする必要があります

val serverInfoCallback = (response: AnyRef) => {
  val serverInfoResponse = response.asInstanceOf[ServerInfoResponse] // explicit cast
  val name = serverInfoResponse.name
  val numberOfCores = serverInfoResponse.numberOfCores
  // so on
}

キャストを回避する方法はありますか?または、コールバックシステムを実装するためのより正しい方法はありますか?

ありがとう!

4

2 に答える 2

4

応答タイプが静的に認識されていない場合は、封印されたトレイト Response を作成し、それらの他のタイプにそれを拡張させることができます。

次に、すべてのケースのチェックに関するいくつかのコンパイラ保証でパターン マッチングを使用できます。これらの型を 1 つのシールされた型に拡張できない場合は、とにかくパターン マッチングを使用できますが、コンパイラは役に立ちません。

応答タイプが静的にわかっている場合、質問でタイプの関係を明確にしていただけますか?

于 2012-10-10T21:14:51.490 に答える
3

あなたの質問は非常に興味深いものであり、信じられないほどの形状のないライブラリを使用してタイプセーフなソリューションを見つけようとしまし. どうぞ:

基本

/* Responses get send to the callbacks */
abstract class Response

/* Callback ids identify callbacks and also specify the type of response
 * a corresponding callback accepts.
 */
abstract class CallbackId[T <: Response]

タイプセーフを保証する暗黙

/* Shapeless magic that ensures a type-safe mapping from identifiers to
 * callbacks. Consider an implicit of type CME[CallbackId[R], R => Unit]
 * as the evidence that "an id promising to identify a callback that
 * accepts a response R actually maps to such a function."
 */
class CME[-K, V] /* CallbackMapEntry */

implicit val acceptAppleResponse =
  new CME[CallbackId[AppleResponse], AppleResponse => Unit]

implicit val acceptPearResponse =
  new CME[CallbackId[PearResponse], PearResponse => Unit]

implicit val acceptAnyResponse =
  new CME[CallbackId[Response], Response => Unit]

反応

/* Define some responses */
case class AppleResponse() extends Response
case class PearResponse() extends Response
case class PruneResponse() extends Response

コールバック

/* Define some callbacks */

val appleResponseCallback1 = (r: AppleResponse) => {
  println("[appleResponseCallback1]")
}

val appleResponseCallback2 = (r: AppleResponse) => {
  println("[appleResponseCallback1]")
}

val pearResponseCallback = (r: PearResponse) => {
  println("[pearResponseCallback]")
}

val anyResponseCallback = (r: Response) => {
  println("[anyResponseCallback] r is a " + r.getClass.getSimpleName)
  r match {
    case appleR: AppleResponse => // ...
    case pearR: PearResponse => // ...
    case pruneR: PruneResponse => // ...
  }
}

識別子

/* A couple of identifiers */
object appleCbId1 extends CallbackId[AppleResponse]
object appleCbId2 extends CallbackId[AppleResponse]
object pearCbId1 extends CallbackId[PearResponse]
object pearCbId2 extends CallbackId[PearResponse]
object someCbId extends CallbackId[Response]

タイプセーフなコールバックのリスト

/* Init list of callbacks */
val callbacks = HMap[CME](
  appleCbId1 -> appleResponseCallback1,
  appleCbId2 -> appleResponseCallback2,
  pearCbId1 -> pearResponseCallback,
  pearCbId2 -> pearResponseCallback,
  someCbId -> anyResponseCallback
)

最初の使用例

val appleCb = callbacks.get(appleCbId1).get
val someCb = callbacks.get(someCbId).get

appleCb(AppleResponse()) /* Fine */
someCb(AppleResponse())  /* Fine */
someCb(PearResponse())   /* Fine */
// appleCb(PruneResponse()) /* Rejected by the compiler */

リクエストを紹介

abstract class Request[R <: Response] {
  def id: CallbackId[R]
}

case class AppleRequest(id: CallbackId[AppleResponse])
  extends Request[AppleResponse]

case class PearRequest(id: CallbackId[PearResponse])
  extends Request[PearResponse]

case class RandomRequest(id: CallbackId[Response])
  extends Request[Response]

2 番目の使用例

def handleAppleRequest(r: AppleRequest) {
  // Do something with the request

  // Phone home
  val cb = callbacks.get(r.id).get
  cb(AppleResponse()) /* Fine */
  // cb(PearResponse())  /* Rejected by the compiler */
}

handleAppleRequest(AppleRequest(appleCbId1))

ソリューションはタイプ セーフである (または少なくともそうしようとしている) ため、「静的」でない環境でコールバックのリストを初期化するのはより複雑になる可能性があります。たとえば、コールバックが (弱く型付けされた) ファクトリまたは反射。

于 2012-10-11T14:22:50.863 に答える