2012-06-15 22 views
6

Chúng tôi đang xây dựng một ứng dụng lớn với logic phức tạp, bao gồm các mô-đun. Tôi đã từng xây dựng các phương pháp quy mô lớn hơn với các phương pháp đơn giản hơn, ví dụ:Viết mã hướng đối tượng tốt dưới 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; 
}; 

(điều này đơn giản là tất nhiên). Kết quả được trả lại, ngoại lệ được ném ra, mọi người đều hạnh phúc.

Bây giờ, chúng tôi đang chuyển sang AnyEvent. mô-đun của tôi là KHÔNG mức cao nhất, vì vậy tôi không thể làm chỉ

module
# fig. 2 
my $cv = AnyEvent->condvar; 
# do stuff 
return $cv->recv; 

Hầu hết AE Tôi đã nhìn thấy cho đến nay hoạt động như thế này:

# fig. 3 
$module->do_stuff($input, 
    on_success => sub { ... }, 
    on_error => sub { ... } 
); 

Vì vậy, tôi thực hiện xong việc viết lại thấp hơn các phương pháp cấp và cố gắng tiến hành với highlevel() và ...

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

Không chính xác. Tôi gọi nó là "cái thang vô hạn". Bây giờ điều tiếp theo tôi có thể đưa ra là một máy trạng thái đặc biệt trong đó highlevel() được chia thành _highlevel_stage1(), _highlevel_stage2() vv Nhưng điều đó cũng không thỏa mãn tôi (điều đó không thể duy trì được), và suy nghĩ về tên tốt thay vì stageXX làm tôi đau đầu).

Chúng tôi đã xem xét một máy trạng thái toàn diện để thúc đẩy toàn bộ ứng dụng, nhưng phải thêm chuyển đổi cho mỗi tương tác có vẻ quá hào phóng với tôi. Vì vậy, câu hỏi là: Các phương pháp hay nhất để viết các mô-đun thực hiện logic nghiệp vụ (hình 1) để chạy trong ứng dụng AnyEvent (hình 3) là gì?

Trả lời

6

Tóm tắt điều hành: bạn muốn đảo ngược quyền kiểm soát (chủ đề với Coro chặn) hoặc máy trạng thái.

Bạn có thể sử dụng Coro, có thể chuyển đổi thang vô hạn thành mã tuyến tính (bằng cách đảo ngược kiểm soát), ví dụ: sử dụng Coro :: rouse_cb/rouse_wait, hay một số các Coro :: chức năng AnyEvent:

do_sth cb => sub { ... 

trở thành (nếu gọi lại chỉ được gọi một lần):

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

chỉ tùy chọn khác của bạn là sử dụng một máy nhà nước, ví dụ sử dụng một đối tượng (có rất nhiều cách để thực hiện máy nhà nước, đặc biệt là trong Perl):

my $state = new MyObject; 

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

Và trong first_step thực hiện, bạn đăng ký một callback với $ tự> next_state_done, vv

Bạn cũng có thể tìm kiếm vào một số mô-đun cpan, chẳng hạn như AnyEvent :: Blackboard hoặc AnyEvent :: Tools - Tôi đã không sử dụng chúng bản thân mình, nhưng có lẽ họ có thể giúp bạn.Đối với condvars, tôi cá nhân không quá nóng về việc sử dụng chúng như là phương tiện duy nhất cho một API - Tôi thích callbacks (như condvars là callbacks hợp lệ này cho phép cả hai), nhưng condvars cho phép bạn để nâng cao ngoại lệ trong người tiêu dùng (thông qua phương thức croak).

+0

Cảm ơn bạn đã làm rõ. Điều này khiến tôi có nhiều câu hỏi hơn ban đầu, nhưng ít nhất giờ tôi có thể tự mình đọc và đọc. – Dallaylaen

3

Vâng, có một điều tôi có thể nghĩ đến là sử dụng Chain chút thay đổi của mô hình Trách nhiệm:

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

Đó là một chút thô, nhưng tôi hy vọng nó cho thấy điểm.)

1

Bạn có thể muốn đóng gói nó trong một đối tượng Tương lai bằng cách sử dụng mô-đun Future. Điều đó bổ sung thêm đường cú pháp để làm sạch hơn.

Các vấn đề liên quan