2012-06-15 5 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 { ... } 
); 

Так что я закончу переписывание нижнего() и ...

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

Не совсем красиво. Я называю это «Бесконечной лестницей».

Теперь следующая вещь, которую я мог придумать, была ad-hoc state machine, где highlevel() разбит на _highlevel_stage1(), _highlevel_stage2() и т. Д. Но это меня не удовлетворяет (это недостижимо, и думать о хороших именах вместо stageXX дает мне головную боль).

Мы уже изучаем полноразмерный конечный автомат для управления всем приложением, но для добавления перехода для каждое взаимодействие выглядит слишком щедрым для меня.

Вопрос: Каковы наилучшие методы написания модулей, реализующих бизнес-логику (рис.1) для работы в приложении AnyEvent (рисунок 3)?

ответ

6

Резюме: вы либо хотите инверсию управления (потоки с Coro, который блокирует), либо конечный автомат.

Вы можете использовать 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 сделано, вы регистрируете функцию обратного вызова для $ self-> next_state_done и т.д.

Вы также можете посмотреть в некоторые модули cpan, такие как AnyEvent :: Blackboard или AnyEvent :: Tools - я сам их не использовал, но, может быть, они могут вам помочь.

Что касается condvars, то я лично не так горяч о том, чтобы использовать их как единственное средство для API - я предпочитаю обратные вызовы (поскольку condvars являются действительными обратными вызовами, это позволяет обоим), но condvars позволяют вам делать исключения в потребителе (через метод каркаса).

+0

Спасибо за разъяснения. Это оставляет мне еще больше вопросов, чем я изначально, но по крайней мере теперь я могу пойти и почитать себя. – Dallaylaen

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

Это немного грубо, но я надеюсь, что она показывает точку.)

1

Возможно, вы захотите инкапсулировать его в объект Future с помощью модуля Future. Это добавляет синтаксический сахар, чтобы сделать это чище.

 Смежные вопросы

  • Нет связанных вопросов^_^