我已经使用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
,这使客户端代码有机会以最合适的方式使用它们(取决于上下文,可以使用tell
或ask
)。
整理您的讯息
编写角色时,应将有关该角色的所有内容都放在一个地方,包括接口(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团队讨论添加一些魔术,但是必须等到它能够获得优先级之后)。将角色的所有属性都定义在一个地方是很重要的部分。