请参阅以下代码,Op1
,Op2
,Op3
所有人都可以在与其他演员沟通时充当消息。
我的问题是,有什么区别,任何建议的使用方式?有些事情可能就像使用Op1更有效率或更少的字节将用于使用其中之一?我想找到一个最佳实践。
package local
import akka.actor._
object Constant {
case object Op1
val Op2 = "msg"
val Op3 = 'msg
}
object Local extends App {
implicit val system = ActorSystem("LocalSystem")
val localActor = system.actorOf(Props[LocalActor], name = "LocalActor")
localActor ! Constant.Op1
localActor ! Constant.Op2
localActor ! Constant.Op3
}
class LocalActor extends Actor {
def receive = {
case Constant.Op1 =>
println("1")
case Constant.Op2 =>
println("2")
case Constant.Op3 =>
println("3")
}
}
答案 0 :(得分:5)
嗯......“最佳实践”一词非常主观。您的问题陈述的背景通常会影响您最佳实践。因此,您应该更多地关注why is this a best practice
而不是what is the best practice
。
现在让我们在通用语境中讨论这个问题。
如您所知,Akka演员使用消息互相沟通。而且你必须明白,无论何时双方之间进行通信,都需要在它们之间建立协议,这使得它们能够以不明确的方式进行通信。
另外,我们知道双方都可以决定在没有任何协议的情况下用英语进行交流,但这会引起彼此误解的可能性。
现在,当我们想要构建可靠的系统时,这种可能存在模糊性的问题是不必要的。这就是为什么我们开始在我们的演员之间建立协议。
object LaughProtocol {
sealed trait LaughMessage
case object Lol extends LaughMessage
case object Rofl extends LaughMessage
case object Lmao extend LaughMessage
}
class LaughActor extends Actor with ActorLogging{
import LaughProtocol._
override def receive = {
case msg @ LaughMessage => handleLaugh(msg)
case msg @ _ => log.info("unexpected message :: {}", msg)
}
def handleLaugh(msg: LaughMessage) = {
case Lol => println("L O L")
case Rofl => println("R O F L")
case Lmao => println("L M A O")
}
}
这使我们能够确定我们将要处理的消息的本质的简单性。另外......对于与LaughActor
进行通信的任何其他演员,它明确规定了与LaughActor
进行通信的规则。
现在......你会说明将这些定义为Strings
会出现什么问题。
class LaughActor extends Actor with ActorLogging{
override def receive = {
case "Lol" => println("L O L")
case "Rofl" => println("R O F L")
case "Lmao" => println("L M A O")
case msg @ _ => log.info("unexpected message :: {}", msg)
}
}
您可以争辩说,即使在这种情况下,任何开发人员都可以查看此actor的代码并找出协议。虽然它们确实可以,但是当查看跨越数十个文件的真实世界代码时......它变得非常困难。
不仅如此......在编写名为compiler
的正确代码时,您失去了一位最重要的帮手。只需考虑以下来自潜在用户LaughActor
。
// case 1 - With sealed hierarchy of messages
laughActorRef ! LuaghProtocol.Lol
// case 2 - with strings,
laughActorRef ! "lol"
如果开发人员犯了错误并写下来而不是上面的
,该怎么办? // case 1 - With sealed hierarchy of messages
laughActorRef ! LuaghProtocol.Loll
// case 2 - with strings,
laughActorRef ! "loll"
在第一种情况下,编译器会立即指出错误...但在第二种情况下,此错误将通过未注意到,并且当隐藏在数万个代码库中时可能会导致很多头痛的调试线。
但是再次......你甚至可以通过仅使用预定义的字符串来避免这个问题,例如关注
object LaughProtocol {
val Lol = "Lol"
val Rofl = "Rofl"
val Lmao = "Lmao"
}
class LaughActor extends Actor with ActorLogging{
import LaughProtocol._
override def receive = {
case msg if msg.equals(LaughProtocol.Lol) => println("L O L")
case msg if msg.equals(LaughProtocol.Rofl) => println("R O F L")
case msg if msg.equals(LaughProtocol.Lmao) => println("L M A O")
case msg @ _ => log.info("unexpected message :: {}", msg)
}
}
但是......考虑一个由5-6名开发人员组成的数十名演员的更大的应用程序。
请注意......到目前为止,开发人员完全依赖于已定义的成员LaughProtocol.Lmao
等......而且他们实际上并不关注LaughProtocol.Lmao
等具有的值。
现在......让我们说另一个演员有像下面这样的协议,
object LoveProtocol {
val LotsOfLove = "Lol"
}
现在......你应该能够注意到这个问题。考虑任何应该处理这两种协议的消息的actor的情况。所有它将收到的是字符串Lol
,它无法知道它是LaughProtocol.Lol
还是LoveProtocol.LotsOfLove
。
所以......现在每当开发人员向任何协议添加任何新消息时,他都需要确保没有其他协议使用相同的String
。对于从事大型代码工作的团队来说,这根本不是一个选择。
这些只是人们在代码库中更喜欢sealed protocols
之类的原因的几个原因。