请参见下面的代码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")
}
}
Run Code Online (Sandbox Code Playgroud)
嗯......"最佳实践"一词非常主观.您的问题陈述的背景通常会影响您最佳实践.因此,你应该更多地关注而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")
}
}
Run Code Online (Sandbox Code Playgroud)
这使我们能够确定我们将要处理的消息的本质的简单性.另外......对于任何其他与之沟通的演员来说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)
}
}
Run Code Online (Sandbox Code Playgroud)
您可以争辩说,即使在这种情况下,任何开发人员都可以查看此actor的代码并找出协议.虽然它们确实可以,但是在查看跨越数十个文件的真实世界代码时......它变得非常困难.
不仅如此......在编写正确的代码时,你失去了一个最重要的帮助者compiler.只需考虑以下来自潜在用户的行LaughActor.
// case 1 - With sealed hierarchy of messages
laughActorRef ! LuaghProtocol.Lol
// case 2 - with strings,
laughActorRef ! "lol"
Run Code Online (Sandbox Code Playgroud)
如果开发人员犯了错误而写了以下而不是上面的内容会怎么样
// case 1 - With sealed hierarchy of messages
laughActorRef ! LuaghProtocol.Loll
// case 2 - with strings,
laughActorRef ! "loll"
Run Code Online (Sandbox Code Playgroud)
在第一种情况下,编译器将立即指出错误...但在第二种情况下,此错误将通过未注意到,并且当隐藏在数万行的代码库中时可能会导致很多头痛的调试.
但是再次......你甚至可以通过仅使用预定义的字符串来避免这个问题
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)
}
}
Run Code Online (Sandbox Code Playgroud)
但是......考虑一个由5-6名开发人员组成的数十名演员编写的更大的应用程序.
请注意......到目前为止,开发人员完全依赖于定义的成员LaughProtocol.Lmao等......而且他们实际上并没有看到等值LaughProtocol.Lmao有什么价值.
现在......让我们说另一个演员有如下的协议,
object LoveProtocol {
val LotsOfLove = "Lol"
}
Run Code Online (Sandbox Code Playgroud)
现在......你应该能够注意到这个问题.考虑任何应该处理这两种协议的消息的actor的情况.所有它将收到的是一个字符串Lol,它将无法知道它是否是一个LaughProtocol.Lol或LoveProtocol.LotsOfLove.
所以......现在每当开发人员向任何协议添加任何新消息时,他都需要确保没有其他协议使用相同的协议String.对于从事大型代码工作的团队来说,这根本不是一个选择.
这些只是为什么人们更喜欢sealed protocols代码库中的东西的原因.