8

モジュールで構成される複雑なロジックを持つ大規模なアプリケーションを構築しています。私は以前、より単純なメソッドのより大規模なメソッドを構築していました。たとえば、

# 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;
};

(これはもちろん単純化されています)。結果が返され、例外がスローされ、誰もが幸せになります。

現在、AnyEvent に移行しています。私のモジュールは最上位レベルではないので、単に行うことはできません

# 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() を進めようとしました...

# fig. 4
package Foo;
sub highlevel {
    my ($self, $user, $event, %callbacks) = @_;
    my $done = $callbacks{on_success};
    my $error = $callbacks{on_error};
    $self->get_session( $user,
        on_error => $error,
        on_success => sub {
             my $session = shift;
             $self->do_stuff( $session, $event,
                  on_error => $error,
                  on_success => sub { 
                       my $result = shift;
                       $self->save_session( $session,
                            or_error => $error,
                            on_success => sub { $done->($result); }
                       );
                  }
              );
          }
     );
 };

正確には美しくありません。私はそれを「無限のはしご」と呼んでいます。

次に思いついたのは、highlevel() が _highlevel_stage1()、_highlevel_stage2() などに分割されたアドホック ステート マシンでした。ステージXXの代わりに名前を付けると頭痛がします)。

私たちはすでにアプリ全体を駆動する本格的なステート マシンを検討していますが、すべてのインタラクションにトランジションを追加する必要があるのは、私には少し寛大すぎるように思えます。

質問は、AnyEvent アプリ (図 3) 内で実行するビジネス ロジック (図 1) を実装するモジュールを作成するためのベスト プラクティスは何ですか?

4

4 に答える 4

8

エグゼクティブ サマリー: 制御の反転 (Coro をブロックするスレッド) またはステート マシンのいずれかが必要です。

Coro::rouse_cb/rouse_wait またはいくつかの Coro::AnyEvent 関数を使用して、(制御の反転により) 無限ラダーを線形コードに変換できる Coro を使用できます。

   do_sth cb => sub { ...

になります (コールバックが 1 回だけ呼び出される場合):

   do_sth cb => Coro::rouse_cb;
   my @res = Coro::rouse_wait;

他の唯一のオプションは、ステート マシンを使用することです。たとえば、オブジェクトを使用します (特に Perl では、ステート マシンを実装する方法は多数あります)。

my $state = new MyObject;

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

first_step done では、$self->next_state_done などのコールバックを登録します。

AnyEvent::Blackboard や AnyEvent::Tools など、いくつかの cpan モジュールを調べることもできます。私自身は使用していませんが、役立つ可能性があります。

condvars に関しては、私は個人的に API の唯一の手段としてそれらを使用することにそれほど熱心ではありません.方法)。

于 2012-06-15T20:35:07.220 に答える
3

さて、私が考えることができる1つのことは、わずかに変更されたChainofResponsibilityパターンを使用することです。

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) },
  );
}

少しラフですが、ポイントがわかるといいですね。)。

于 2012-06-15T13:04:03.670 に答える
3

Futureモジュールを使用して Future オブジェクトにカプセル化することもできます。これにより、シンタックス シュガーが追加され、これがよりクリーンになります。

于 2014-11-28T12:00:13.247 に答える