2014-12-21 49 views
4

我剛開始學習Akka/Scala,並且我寫了一個小型聊天服務器。在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") 
    } 
} 

好了,好了,它的工作原理...但是...在那裏爲我訪問一個演員裏面那些一成不變會員更方便地什麼辦法?我試圖讓一個包裝供房的演員,像下面的代碼:

case class RoomWrapper(val id: Int, val name: String, actor: ActorRef) 

似乎不錯,但有一個問題:現在我不得不到處傳遞RoomWrapper對象。當房間被毀壞時我怎樣才能得到通知?我不能context.watch a RoomWrapper

如何解決這個問題?有沒有可能讓我這樣寫?

val rooms: List[ActorRef] 
rooms map { room => 
    println(room.id) 
    println(room.name) 
} 
+0

這樣做反過來呢? 'Actor'中的'case class ImmutableRoomData(id:Int,name:String)',然後你只需要檢索一次,就可以避免'zip'。此外,地圖序列組合可以寫得更簡潔,如「遍歷」。 – lmm

+0

@Imm我認爲ImmutableRoomData的想法基本上與提到的一個kaktusito元組相同,但「遍歷函數」是一個非常好的點!我應該使用它! –

回答

2

嗯,這只是我的看法。我認爲你不應該按照你的建議去做,因爲那樣會使你的代碼變得「多樣化」(我的意思是,從演員模型變成更具體的東西)。從技術上講,演員不應該分享任何狀態,而且他們應該只對事件(消息)做出反應。在任何情況下,使用推導,你可以重寫上面爲:

for { 
    room <- rooms 
    id <- (room ? GetId).mapTo[Int] 
    name <- (room ? GetName).mapTo[String] 
} { 
    println(id) 
    println(name) 
} 

而且,你可以把它返回兩個編號,名稱和一個元組的消息,依此類推。可能性是無限的,但我不會允許以這種方式直接訪問甚至不可變的狀態,因爲它讓我對我正在編碼的應用程序的特定模式有所瞭解。