2012-06-15 7 views
6

私たちはモジュールで構成された複雑なロジックを持つ大規模なアプリケーションを構築しています。私はより単純な方法のより大規模な方法を構築していました。たとえば、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_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 { ... } 
); 

だから私は、下の書き換えを行っています-levelメソッドを実行して、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()などに分割されたアドホック状態マシンを考えました。しかし、それは私にも受け入れられませんstageXの代わりに良い名前を考えると頭痛になる)

私たちはすでにアプリケーション全体を動かすために本格的な状態マシンを探していますが、に遷移を追加するごとに、のやり取りはあまりにも寛大です。

質問:AnyEventアプリケーション(図3)内で実行するためのビジネスロジック(図1)を実装するモジュールを作成するためのベストプラクティスは何ですか?それは少し荒いですが、私はそれがポイントを示し願ってい

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

答えて

6

エグゼクティブサマリー:制御の逆転(ブロックを持つスレッドのスレッド)または状態マシンを使用します。

無限ラダーを線形コード(制御の反転によって)に変換できるCoroを使用できます。コロ:: rouse_cb/rouse_waitを使用して、またはコロ:: AnyEvent機能の一部:(コールバックが一度だけ呼び出された場合)

do_sth cb => sub { ... 

は次のようになります。

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で行われ、あなたは$自己のためにコールバックを登録する> next_state_doneなど

をあなたも見ることができますAnyEvent :: BlackboardやAnyEvent :: Toolsのようなcpanモジュールには、私は自分で使っていませんが、おそらく彼らはあなたを助けることができます。

私は個人的にAPIのための唯一の手段としてそれらを使用することについて熱心ではありません - 私はコールバックを好む(condvarsは有効なコールバックであり、これは両方を許可します)、しかしcondvarsは消費者で例外を発生させます(クローク法による)。

+0

説明していただきありがとうございます。これは私が最初に持っていたよりもさらに多くの質問を私に残すが、少なくとも今私は自分自身を読んで読むことができる。 – Dallaylaen

3

まあ、私は考えることができる一つのことは責任パターンを少し変更したチェーンを使用しています。 )

1

Futureモジュールを使用してFutureオブジェクトにカプセル化したい場合があります。それはこのクリーナーを作るために構文的な砂糖を加えます。

関連する問題