民间, 我已经定义了以下地图和结构类型:
type Mergeable = { def mergeFrom(data: Array[Byte]): com.google.protobuf.GeneratedMessageLite }
val dispatchMap = Map(
1 -> (ZombieSighting.defaultInstance.asInstanceOf[Mergeable], "akka://UlyssesAgenda/user/ServerCore/DispatchTarget")
)
基本上我正在做的是定义一个地图,上面写着“当我从线路上读取protobuf消息类型1时,从字节创建一个ZombieSighting,并将其发送给在指定字符串处找到的actor”。
所以,这是我今天创建的消息,一个actor,并将消息发送给actor的代码:
val dispatchInfo = dispatchMap.get(messageType)
val theMessage = dispatchInfo.map { _._1.mergeFrom(theBytes) }.get
val actorPath = dispatchInfo.map { _._2 }
val targetActor = actorPath.map { SocketServer.system.actorFor(_) }
targetActor.map { _ ! theMessage }
当我看到这个时,我能想到的是这个看起来有多少非功能性编程,我不禁想到有更优雅的方法来做到这一点。我知道我可以编写将元组作为参数并返回修改后的元组的函数,但我认为这里没有任何关于干净的惯用scala的东西。
我的直觉说我可以在“dispatchMap.get(messageType)”上运行一个map语句,它将根据tuple._2和基于tuple._1的合并protobuf对象给出一个Akka actor的实例。
有人可以提出一种方法来对这些代码进行操作,减少行数并使其更具功能性吗? e.g。
val (targetActor, theMessage) = ????
targetActor ! theMessage
编辑:这是我对重构的第一次尝试..这是“Scala方式”吗?
val (msg, actor) = dispatchMap.get(messageType).map {
case (m:Mergeable, path:String) =>
(m.mergeFrom(theBytes), SocketServer.system.actorFor(path) )
}.get
actor ! msg
编辑2:这是以下评论者建议的版本,但我不知道这是否真的是惯用的:
def dispatchMessage: IO.Iteratee[Unit] =
repeat {
for {
(messageType, byteString) <- readMessage
} yield {
for (
(protoMessage, actorPath) <- dispatchMap.get(messageType);
theMessage = protoMessage.mergeFrom(byteString.toArray);
targetActor = SocketServer.system.actorFor(actorPath) )
yield
targetActor ! theMessage
}
}
答案 0 :(得分:3)
您的代码可能是这样写的:
val akkaResponseFuture =
for ((bytes, actorPath) <- dispatchMap.get(messageType);
theMessage <- bytes.mergeFrom(theBytes);
targetActor = SocketServer.system.actorFor(actorPath))
yield
targetActor ! theMessage
这可能是不对的。我没有尝试创建工作代码就这样做了,因为你的样本不是自包含的。此外,theBytes
无法定义。
但我很确定你可以使用for comprehension来澄清和简化它。