为了能够处理大量不同的请求类型,我创建了一个.proto
文件,如下所示:
message Message
{
string typeId = 1;
bytes message = 2;
}
我添加了typeId
,以便了解实际protobuf
bytes
代表什么。 (自描述)
现在我的问题是以优雅的方式处理不同的“具体类型”。 (注意:如果我简单地使用类似switch-case
的方法,一切正常!)
我想到了这样的解决方案:
1)具有不同处理者必须实现的特征,例如:
trait Handler[T]
{
def handle(req: T): Any
}
object TestHandler extends Handler[Test]
{
override def handle(req: Test): String =
{
s"A success, $req has been handled by TestHandler
}
}
object OtherHandler extends Handler[Other]
{
override def handle(req: Other): String =
{
s"A success, $req has been handled by OtherHandler
}
}
2)提供某种注册表来查询给定消息的正确处理程序:
val handlers = Map(
Test -> TestHandler,
Other -> OtherHandler
)
3)如果一个请求进入它标识自己,所以我们需要另一个Mapper:
val reqMapper = Map(
"Test" -> Test
"Other" -> Other
)
4)如果有请求,请处理:
val request ...
// Determine the requestType
val requestType = reqMapper(request.type)
// Find the correct handler for the requestType
val handler = handlers(requestType)
// Parse the actual request
val actualRequest = requestType.parse(...) // type of actualRequest can only be Test or Other in our little example
现在,直到这里一切看起来都很精致,但是这条线打破了我的整个世界:
handler.handle(actualRequest)
导致:
类型不匹配;发现:com.trueaccord.scalapb.GeneratedMessage with Product with com.trueaccord.scalapb.Message [_>:tld.test.proto.Message.Test with tld.test.proto.Message.Other<:com.trueaccord。 scalapb.GeneratedMessage with Product] with com.trueaccord.lenses.Updatable [_>:tld.test.proto.Message.Other with tld.test.proto.Message.Test&lt ;: com.trueaccord.scalapb.GeneratedMessage with Product ] {def companion:Serializable}必需:_1
据我了解 - 如果我错了请认真对待 - 编译器在这里无法确定,actualRequest
是否由处理程序“处理”。这意味着它缺乏actualRequest
肯定位于mapper
的某个地方的知识,而且还有handler
。
它基本上是人类会得到的隐含知识,但编译器无法推断。
那就是说,我怎么能优雅地克服这种情况呢?
答案 0 :(得分:2)
使用普通地图时,您的类型会丢失。例如
object Test{}
object Other{}
val reqMapper = Map("Test" -> Test,"Other" -> Other)
reqMapper("Test")
res0: Object = Test$@5bf0fe62 // the type is lost here and is set to java.lang.Object
最常见的方法是使用模式匹配
request match {
case x: Test => TestHandler(x)
case x: Other => OtherHandler(x)
case _ => throw new IllegalArgumentException("not supported")
}
如果您仍想使用Google地图将类型存储到处理程序关系,请考虑Shapeless here提供的HMap
异质地图
Shapeless提供支持任意的异质地图 密钥类型与相应的值类型之间的关系,
答案 1 :(得分:1)
您可以使用的一个技巧是将伴随对象捕获为隐式对象,并将解析和处理组合在一个函数中,其中该类型可供编译器使用:
case class Handler[T <: GeneratedMessage with Message[T]](handler: T => Unit)(implicit cmp: GeneratedMessageCompanion[T]) {
def handle(bytes: ByteString): Unit = {
val msg: T = cmp.parseFrom(bytes.newInputStream)
handler(t)
}
}
val handlers: Map[String, Handler[_]] = Map(
"X" -> Handler((x: X) => Unit),
"Y" -> Handler((x: Y) => Unit)
)
// To handle the request:
handlers(request.typeId).handle(request.message)
另外,请查看any.proto
,它定义了与您的Message
非常相似的结构。它无法解决您的问题,但您可以利用它的pack
和unpack
方法。
答案 2 :(得分:0)
我现在已经解决了这个问题(基本上是相同的,有点适合我的特定用例)
trait Handler[T <: GeneratedMessage with Message[T], R]
{
implicit val cmp: GeneratedMessageCompanion[T]
def handle(bytes: ByteString): R = {
val msg: T = cmp.parseFrom(bytes.newInput())
handler(msg)
}
def apply(t: T): R
}
object Test extends Handler[Test, String]
{
override def apply(t: Test): String = s"$t received and handled"
override implicit val cmp: GeneratedMessageCompanion[Test] = Test.messageCompanion
}