使用akka发送对象,字符串,符号的区别是什么?

时间:2016-10-19 09:02:32

标签: scala akka

请参阅以下代码,Op1Op2Op3所有人都可以在与其他演员沟通时充当消息。

我的问题是,有什么区别,任何建议的使用方式?有些事情可能就像使用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")
  }
}

1 个答案:

答案 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之类的原因的几个原因。