Perl 在AnyEvent下编写良好的面向对象代码

Perl 在AnyEvent下编写良好的面向对象代码,perl,asynchronous,anyevent,Perl,Asynchronous,Anyevent,我们正在构建一个具有复杂逻辑的大型应用程序,它由模块组成。我曾经用更简单的方法构建更大规模的方法,例如 # fig. 1 package Foo; sub highlevel { my ($self, $user, $event) = @_; my $session = $self->get_session($user); my $result = $self->do_stuff($session, $event); $self->save

我们正在构建一个具有复杂逻辑的大型应用程序,它由模块组成。我曾经用更简单的方法构建更大规模的方法,例如

# fig. 1   
package Foo;
sub highlevel {
    my ($self, $user, $event) = @_;
    my $session = $self->get_session($user);
    my $result = $self->do_stuff($session, $event);
    $self->save_session($session);
    return $result;
};
(这当然是简化的)。结果被返回,异常被抛出,每个人都很高兴

现在,我们正在进行任何活动。我的模块不是最高级别的,所以我不能只做

# fig. 2
my $cv = AnyEvent->condvar;
# do stuff
return $cv->recv;
到目前为止,我看到的大多数AE模块都是这样工作的:

# fig. 3
$module->do_stuff( $input, 
    on_success => sub { ... }, 
    on_error => sub { ... }
);
我已经重写了低级方法,并尝试继续使用highlevel()和

不太漂亮。我称之为“无限阶梯”

现在我能想到的下一件事是一个特殊的状态机,其中highlevel()被分为_highlevel_stage1(),_highlevel_stage2()等等。但这也不能让我满意(它无法维护,想到好名字而不是stageXX让我头疼)

我们已经在寻找一个成熟的状态机来驱动整个应用程序,但是在我看来,必须为每个交互添加一个转换看起来有点过于慷慨了


因此,问题是:编写实现业务逻辑(图1)的模块以在AnyEvent应用程序(图3)中运行的最佳实践是什么

嗯,我能想到的一件事是使用稍加修改的责任链模式:

my $params = {
  user => $user,
  event => $event,
  session => undef
};

my @chain = ('get_session', 'do_stuff', 'save_session', 'emit');

for my $index (0..$#chain) {
  my $current = $chain[$index];
  my $next    = $chain[$index + 1] || undef;
  $self->{$current}($params, 
    on_error => sub { $self->error($params) },
    on_success => sub { $self->{$next}($params) },
  );
}

这有点粗糙,但我希望它能说明问题。)

执行摘要:您要么想要控制反转(带有Coro块的线程),要么想要状态机

您可以使用Coro,它可以将无限梯形图转换为线性代码(通过控制反转),例如,使用Coro::rouse_cb/rouse_wait,或一些Coro::AnyEvent函数:

   do_sth cb => sub { ...
变为(如果只调用一次回调):

您唯一的另一种选择是使用状态机,例如使用对象(有许多方法可以实现状态机,特别是在Perl中):

在第一步完成时,注册$self->next\u state\u done等的回调

您还可以查看一些cpan模块,例如AnyEvent::Blackboard或AnyEvent::Tools—我自己没有使用过它们,但它们可能会帮助您


至于condvar,我个人并不热衷于将它们作为API的唯一手段——我更喜欢回调(因为condvar是有效的回调,这两者都允许),但condvar确实允许您在使用者中引发异常(通过croak方法)。

您可能希望使用该模块将其封装在未来的对象中。这就增加了语法上的糖分,使它更干净

7年后,当我访问这个问题时,我似乎知道正确的答案:。协同路由、Future、Promise、async/await和(一个合理的)有限状态机实现都是这类的子类型

当然,这会阻止我向其他人解释


如果我今天必须再做一次,我会选择一些承诺对象。

谢谢你的澄清。这给我留下了比最初更多的问题,但至少现在我可以自己去读。“某种承诺对象”正是
Future
提供的。@melpomene是的,我根据这个理由更改了选择的答案。虽然我没有参与我要求的项目,但7年的时间太长了。啊,我不知道你改变了他们接受的答案。
   do_sth cb => sub { ...
   do_sth cb => Coro::rouse_cb;
   my @res = Coro::rouse_wait;
my $state = new MyObject;

do_something callback => sub { $state->first_step_done };