【发布时间】:2015-02-19 16:29:31
【问题描述】:
刚开始学习Akka/Scala,自己写了一个小聊天服务器。
想象这是一个基于房间的聊天服务器,每个人都可以创建自己的房间,并且可以同时在多个房间中。每当一个房间的成员用完时,房间就会关闭。房间由id: Int 标识,并具有不可变的name: String。我写了以下代码来展示房间。
class Room(val id: Int, val name: String, var host: ActorRef) extends Actor {
def receive = {
case GetId() =>
sender ! id
case GetName() =>
sender ! name
case AddMember(member) => ...
case RemoveMember(member) => ...
case BroadcastMessage(from, message) => ...
}
现在,客户端需要所有房间的 ID 和名称来决定加入哪个房间。
val rooms: List[ActorRef] // Obtained somewhere
val getIdFutures: List[Future[Int]] = rooms.map { (_ ? GetId()).mapTo[Int] }
val getNameFutures: List[Future[String]] = rooms.map { (_ ? GetName()).mapTo[String] }
val getIds: Future[List[Int]] = Future.sequence(getIdFutures)
val getNames: Future[List[String]] = Future.sequence(getNameFutures)
for (ids <- getIds; names <- getNames) yield {
ids zip names map { pair =>
val id = pair._1
val name = pair._2
println(s"$id: $name")
}
}
好吧,好吧,它可以工作...但是...有什么方法可以让我更方便地访问 Actor 内的那些 不可变 成员?我尝试为房间演员制作一个包装器,如下面的代码:
case class RoomWrapper(val id: Int, val name: String, actor: ActorRef)
看起来不错,但有一个问题:现在我必须到处传递RoomWrapper 对象。房间被破坏时如何得到通知?我不能context.watchRoomWrapper!
如何解决这个问题?我有可能这样写吗?
val rooms: List[ActorRef]
rooms map { room =>
println(room.id)
println(room.name)
}
【问题讨论】:
-
反过来呢?
case class ImmutableRoomData(id: Int, name: String)在Actor中,然后你只需要检索一次就可以避免zip。此外,map-sequence 组合可以更简洁地写为 scalaztraverse。 -
@Imm 我认为 ImmutableRoomData 的想法和 kaktusito 提到的元组基本相同,但是
traverse function是一个非常好的点!我应该使用它!
标签: scala akka immutability actor future