我已经使用Akka和Scala大约一个月了,我为用消息替换显式接口(interface)感到有些困扰。考虑以下简单的Akka Actor:

case class DoMyHomework()
class Parent extends Actor {
  def receive = {
    case d: DoMyHomework => // do nothing
  }
}

发送此actor DoMyHomework消息的Actor或非actor代码,如下所示:
ActorRef parent = ...
parent.ask(DoMyHomework)

不知道结果会怎样。答案是什么类型?我会得到答案吗?我可以异常(exception)吗?等等。

解决方法似乎是记录案例类……但是如果其他参与者也收到相同的案例类怎么办。然后,用于接收该消息的文档应该在参与者本身中。

为了解决这个问题,我想到了以下操作:
trait SomeoneSmarter {
  def wouldYouDoMyHomework: Future[Boolean]
}
class Parent extends Actor with SomeoneSmarter {
  case class DoMyHomework()
  def wouldYouDoMyHomework = {
    (self ? DoMyHomework()).mapTo(Boolean)
  }
  def receive = {
    case d: DoMyHomework =>
      // TODO: If I'm busy schedule a false "No way" reply for a few seconds from now.
      // Just to keep their hopes up for a while. Otherwise, say sure right away.
  }
}

因此,我就此事与同事聊天,反应之一是“您对 Actor 模型并不忠实”。

首先,我真的很感谢长期使用Actor的人们的一些指导。所有消息都变得笨拙吗?您最终将消息传递隐藏在接口(interface)后面吗?

我提议的 Actor 仍然可以选择在彼此之间发送消息,订阅事件流以及Akka期望的所有内容。该界面为您提供了一种经过时间考验的方式来了解您在说什么。并且在IDE等中进行编码时会有所帮助。以及为什么 Actor 的用户需要知道它是一个 Actor (除非它也是一个 Actor 并且与它紧密地联系在一起)?

我得到的另一个反应是“看起来您想要一个TypedActor”。但是在阅读了有关TypedActor的内容后,我并没有说服。当然,TypedActor避免了创建这些内部消息的麻烦。但是,至少从以下代码示例中
http://doc.akka.io/docs/akka/snapshot/scala/typed-actors.html给人的印象是TypedActor仅打算作为您要封装的代码块周围的代理工作,或者使线程安全,或者干脆不直接从当前线程中调用。而您编写的代码仅仅是实现和接口(interface)。您不会惹恼 Actor 本身(代理人)-例如如果您希望您的实现执行定期工作或订阅事件流,或执行与接口(interface)无关的其他任何操作。

我还阅读了http://letitcrash.com/post/19074284309/when-to-use-typedactors,但没有发现该示例更具启发性。我可能不是在 mock TypedActor(不是我声称已经真正了解Actors)。

在此先感谢您的帮助。

皮诺

最佳答案

Actor 封装

让我首先回应我认为非常重要的一点。你说:



Actor是与传统OO截然不同的编程范例,主要区别是所有事物都是异步的,因此永远不会有真正的“返回值”。这意味着隐藏一个事实是通常不是一个好主意,因为异常(exception)情况请引用my TypedActors blog post。关于actor的最好的事情是,它们被完全封装(在ActorRef之后的Akka中),而不是OO语言的弱封装。为了最大程度地利用它,请尽可能公开ActorRef,这使客户端代码有机会以最合适的方式使用它们(取决于上下文,可以使用tellask)。

整理您的讯息

编写角色时,应将有关该角色的所有内容都放在一个地方,包括接口(interface)契约(Contract)描述。它可能看起来像这样:

object Parent {
  /**
   * Send this message to make your parent do your homework … yeah, right ;-)
   */
  case object DoHomework
}

/**
 * This actor will do your homework if asked to.
 *
 * ==Actor Contract==
 *
 * ===Inbound Messages===
 *  - '''DoHomework''' will ask to do the homework
 *
 * ===Outbound Messages===
 *  - '''HomeworkResult''' is sent as reply to the '''DoHomework''' request
 *
 * ===Failure Modes===
 *  - '''BusinessTripException''' if the parent was not home
 *  - '''GrumpyException''' if the parent thinks you should do your own homework
 */
class Parent extends Actor {
  …
}

与TypedActor的区别

使用普通的非类型化actor,您可以利用actor模型的全部功能,包括动态更改行为,而不会试图将自己放入“同步”调用的超时保护的笼子中(简而言之,TypedActor在以下情况下最有用:使用幕后的 Actor 来实现传统的同步界面)。我同意IDE对消息类型的支持会很好,但这是一个工具问题(我一直在与ScalaIDE团队讨论添加一些魔术,但是必须等到它能够获得优先级之后)。将角色的所有属性都定义在一个地方是很重要的部分。

09-10 12:28