2010-06-17 39 views
5

Tôi đang cố viết một vai trò đơn lẻ bằng Perl và Moose. Tôi hiểu một mô-đun MooseX :: Singleton có sẵn nhưng luôn luôn có sức đề kháng khi yêu cầu một mô-đun CPAN khác cho dự án của chúng tôi. Sau khi thử điều này và có một chút rắc rối tôi muốn hiểu TẠI SAO phương pháp của tôi không hoạt động. Vai trò đơn tôi đã viết như sau:Các vai trò Singleton trong Moose

package Singleton; 
use Moose::Role; 

my $_singleInstance; 

around 'new' => sub { 
    my $orig = shift; 
    my $class = shift; 
    if (not defined $_singleInstance){ 
     $_singleInstance = $class->$orig(@_); 
    } 
    return $_singleInstance; 
}; 

sub getInstance 
{ 
    return __PACKAGE__->new(); 
} 

1; 

Điều này dường như chỉ hoạt động khi chỉ có một lớp sử dụng vai trò đơn. Tuy nhiên khi hai lớp (ClassA và ClassB ví dụ) cả hai tiêu thụ vai trò Singleton nó xuất hiện khi cả hai đều đề cập đến một biến $ _singleInstance chia sẻ. Nếu tôi gọi ClassA-> getInstance nó sẽ trả về một tham chiếu đến một đối tượng ClassA. Nếu tôi gọi ClassB-> getInstance đôi khi trong cùng một kịch bản nó trả về một tham chiếu đến một đối tượng kiểu ClassA (mặc dù tôi đã gọi rõ ràng phương thức getInstance cho ClassB). Nếu tôi không sử dụng một vai trò và thực sự sao chép và dán mã từ vai trò Singleton vào ClassA và ClassB nó xuất hiện để làm việc tốt. Những gì đang xảy ra ở đây?

+1

Bạn nhận ra rằng gói 'mới' chỉ yêu cầu một thế giới bị tổn thương, phải không? – Ether

Trả lời

3

Bạn đang lưu thể hiện trên tất cả các loại, thay vì sử dụng một phiên bản khác cho từng loại lớp.

này đòi hỏi các mẫu thiết kế nhà máy, ví dụ:

package MyApp::Factory; 

my %instances; 

# intantiates an object instance if there is none available, 
# otherwise returns an existing one. 
sub instance 
{ 
    my ($class, $type, @options) = @_; 

    return $instances{$type} if $instances{$type}; 
    $instances{$type} = $type->new(@options); 
} 

Nếu bạn thực sự muốn độc thân, hãy cài đặt MooseX :: Singleton chứ không phải là cán của riêng bạn - nếu bạn nhìn vào nguồn bạn sẽ thấy nó chiếm nhiều trường hợp cạnh. Tuy nhiên, tôi khuyên bạn không nên ép buộc các lớp của bạn trở thành những người độc thân, vì điều đó sẽ loại bỏ sự kiểm soát khỏi chính lớp đó. Thay vào đó, hãy sử dụng một nhà máy (như trên), vì vậy người gọi có thể quyết định cách xây dựng lớp, thay vì buộc tất cả người tiêu dùng vào một nhóm.

1

Họ đang chia sẻ biến mẫu. Bạn cần phân bổ nó bên trong gói bằng cách sử dụng vai trò.

# find storage for instance 
my $iref = \${ "${class}::_instance" }; 

# an instance already exists; return it instead of creating a new one 
return $$iref if defined $$iref; 

# no instance yet, create a new one 
... 
+2

Nếu bạn định đi theo lộ trình đó, sử dụng vai trò metaclass có lẽ mạnh hơn rất nhiều so với khỉ với bảng biểu tượng. – friedo

3

bạn $_singleInstance được lexically scoped vào khối nơi nó xuất hiện, trong trường hợp này toàn bộ Singleton gói của bạn. Công cụ sửa đổi around của bạn tạo thành một đóng cửa trên biến này, có nghĩa là nó hiển thị cùng một$_singleInstance mỗi khi nó được chạy, bất kể lớp nào được tạo thành.

Một cách đơn giản để giải quyết này sẽ được lưu trữ độc thân của bạn trong một hash:

my %_instances; 

around 'new' => sub { 
    my $orig = shift; 
    my $class = shift; 
    if (not defined $_instances{$class}){ 
     $_instances{$class} = $class->$orig(@_); 
    } 
    return $_instances{$class}; 
}; 

Có thể là một cách tốt hơn sẽ được thiết lập vai trò tùy chỉnh metaclass mà các cửa hàng dụ singleton cho mỗi lớp mà tiêu thụ vai trò đó.

+0

Tôi đã tìm ra nó là một vấn đề với biến bị scoped với vai trò.Đây chỉ là một chút bối rối vì __PACKAGE__ dường như đề cập đến gói tiêu thụ vai trò và không phải là gói/vai trò "Sngleton". Tôi đã hy vọng rằng bất kỳ biến nào được xác định trong một vai trò sẽ được scoped đến gói tiêu thụ chúng và không phải là gói của vai trò. – mjn12

+1

Moose không ảnh hưởng đến cách Perl phân tích cú pháp hoặc hiểu các biến. Không có cách nào dễ dàng để thực hiện hành vi mà bạn đang giả định ở đây, và sẽ hoàn toàn nằm ngoài phạm vi của Moose. – perigrin

2

"Tôi hiểu một module MooseX :: Singleton có sẵn nhưng luôn có sức đề kháng khi đòi hỏi một mô-đun CPAN cho dự án của chúng tôi."

Đây thực sự là một cái gì đó mà cần phải được giải quyết. Là một dep, MX: Singleton là rất nhỏ. Vấn đề là gì? Bạn có bị mắc kẹt trên một Perl được chia sẻ trên toàn cầu trên một máy chủ được chia sẻ hoặc tương tự không? Nếu vậy, bạn thực sự nên xem xét địa phương :: lib, được thiết kế để làm cho nó dễ dàng cho các nhà phát triển cá nhân để quản lý CPAN phụ thuộc đúng với một kịch bản Makefile.PL, giống như bất kỳ mô-đun CPAN khác.

+1

Cảm ơn bạn đã dành thời gian trả lời câu hỏi, tuy nhiên điều này thực sự không hữu ích. Tôi yêu cầu giúp đỡ để hiểu tại sao mã đặc biệt này không hoạt động và thừa nhận sự tồn tại của MooseX :: singleton để tránh chính xác kiểu phản ứng này. – mjn12

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