我刚开始学习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.watch
一个RoomWrapper
!
如何解决这个问题?我有可能这样写吗?
val rooms: List[ActorRef]
rooms map { room =>
println(room.id)
println(room.name)
}
答案 0 :(得分:2)
嗯,这只是我的意见。我不认为你应该按照你的建议去做,因为那样会使你的代码“多样化”(我的意思是,从演员模型中变成更具体的东西)。从技术上讲,演员不应该共享任何州,而且他们应该只对事件(消息)做出反应。无论如何,使用for comprehension可以将上面的内容重写为:
for {
room <- rooms
id <- (room ? GetId).mapTo[Int]
name <- (room ? GetName).mapTo[String]
} {
println(id)
println(name)
}
此外,您可以创建一条消息,将id和name作为元组返回,依此类推。可能性是无限的,但我不允许以这种方式直接访问甚至是不可变状态,因为它使我对编码进入应用程序的特定模式的视觉有点混乱。