要在Akka(Java绑定)中实现自己的自定义actor,可以扩展UntypedActor
基类。这需要您定义自己的onReceive(...)
方法:
@Override
public void onReceive(Object message) {
// TODO
}
目前的问题是确定一种消息处理策略,使演员能够处理多种类型的消息。一种策略是使用反射/类型。这里的问题是:
message
参数并阻止我们传递任何动态或有意义的内容空shell类的示例:
public class EmptyShellMessage { }
然后在onReceive
方法中看起来像:
@Override
public void onReceive(Class<?> message) {
if(message.isAssignableFrom(EmptyShellMessage.class)) {
// TODO
} else {
// TODO
}
}
因此,我们不仅创建了一个无用的类,而且由于Object message
现在被用于传达消息的类/类型,我们不能使用它来包含更多信息,尤其是另一个角色可能想要传递的动态/运行时信息。
有时候我会看到一个变体:
@Override
public void onReceive(Object message) {
if(message instanceof FizzEvent) {
// TODO
} else {
// TODO
}
}
但是在这里我们使用instanceof
被许多视为巨大的反模式(只是google“ instanceof antipattern < / em>的“)。
然后我们有枚举:
public enum ActorMessage {
FizzEvent,
BuzzEvent,
FooEvent,
BarEvent
}
现在onReceive
看起来像:
@Override
public void onReceive(ActorMessage message) {
if(message.equals(ActorMessage.FizzEvent)) {
// TODO
} else {
// TODO
}
}
这里的问题是我们可能有一个大型演员系统,需要处理数百甚至数千种不同的事件/消息类型。这个枚举变得很大并且难以维护。它也有与上面的反射策略相同的问题,它阻止我们在演员之间发送任何动态信息。
我能想到的最后一件事是使用字符串:
@Override
public void onReceive(String message) {
if(message.equals("FizzEvent")) {
// TODO
} else {
// TODO
}
}
但我讨厌这个。期。句末。
所以我问:我在这里错过了一些明显的东西,也许是另一种策略? Java / Akka应用程序应该如何处理大量事件/消息类型,并指定它们在onReceive
方法中处理哪一个?
答案 0 :(得分:6)
不,你没有遗漏任何东西。我也不是粉丝。在Scala中它更好一点,因为onReceive方法可以被换出来模拟协议的变化状态,它使用的部分函数比if / elseif / else好一点......但它仍然很蹩脚。它在Erlang中更好,这是该模型的起源,但考虑到akka团队所面临的局限,他们做出了正确的设计选择并做得非常出色。
另一种策略是执行双重调度。因此,将命令传递给actor作为动作,或者在map中查找消息的处理程序。 Akka特工基本上是前者,当他们的力量使用时非常好。但总的来说,双重调度只会增加复杂性,因此对于大多数情况,人们只需要习惯标准方法。 java中的含义是instanceof还是switch语句。
双重调度(伪代码)的示例,此处包含完整性,以帮助理解。作为一种方法,它带有健康警告,所以我重申;标准方法是标准的原因,并且应该使用99%的时间。
onReceive( msg ) {
msg.doWork()
}
这种方法的问题在于,现在消息需要知道如何处理自己,这很脏;它会引起紧密耦合并且可能很脆弱。呸。
因此我们可以使用查找处理程序(命令模式样式)
val handlers = Map( "msgid1"->Handler1, "msgid2->Handler2 )
onReceive( msg ) {
val h = handlers(msg.id)
h.doWork( msg )
}
这里的问题是现在还不清楚命令是什么,并且遵循代码通过涉及跳转更多。但有时候这是值得的。
正如Roland所指出的,在传递对演员本身的引用时必须小心。上面的例子并没有违背这个问题,但这很容易诱惑。
答案 1 :(得分:1)
我强烈建议不要传递Actor的this
引用,因为这很容易让你将它传递到执行上下文边界,这可能以完全不显眼的方式发生。另一个问题是它需要消息类型知道actor如何处理它,这与消息传递应该如何解耦不同实体完全相反:Actor必须选择如何处理消息,而不是其他方式周围。
Actors是动态实体,它们可以以不可预见的顺序接收输入(这是模型的核心),因此我们必须使用动态语言功能来实现它们。 instanceof
是促进消息类型的运行时发现的语言的一部分。无论它是否在其他环境中被滥用,并且与谁称之为反模式无关,它正是这项工作的正确工具。其他语言中的Actor实现 - 包括古老的Erlang使用模式匹配以实现同样的效果,模式匹配与instanceof
测试完全相同(加上更方便的参数提取)。
答案 2 :(得分:0)
我为Java 8创建了一个匹配DSL的简单模式,这对Akka演员很有用:https://github.com/strangepleasures/matchmetender
public class MatchingActor extends UntypedActor {
private LoggingAdapter log = Logging.getLogger(getContext().system(), this);
public void onReceive(Object message) throws Exception {
match(message,
(Foo foo) -> log.info("received a Foo: " + foo),
(Bar bar) -> log.info("received a Bar: " + bar),
(Baz baz) -> log.info("received a Baz: " + baz),
(unknown) -> unhandled(unknown)
);
}
}
答案 3 :(得分:0)
我同意框架会促使您做一些根本不优雅的事情,并且我不会弄脏编写此类代码的手指。 所以我的方法是这样。 1)接口:
public interface IAnswerable {
Object getAnswer();
}
2)演员:
@Override
public void onReceive(Object message) throws Throwable {
IAnswerable iAnswerable = (IAnswerable) message;
getSender().tell(iAnswerable.getAnswer(), getSelf());
}
3)我用作消息的类:
public class MyClass implements IAnswerable {
@Override
public Object getAnswer() {
// Whatever you need to do here
return null;
}
}
这是一种原始方法,但是您可以理解。它优雅且易读,可防止演员壮大。
希望它会有所帮助;)