伙计们,我已经定义了以下地图和结构类型:
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,并将其发送给在指定字符串中找到的演员”。
所以,这就是我今天的代码,它创建一个消息,一个演员,并将消息发送给演员:
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 的 Akka actor 实例和一个基于 tuple._1 的合并 protobuf 对象。
有人可以建议一种方法来拉皮条此代码并减少行数并使其更具功能性吗?例如
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
}
}
?