2009-08-07 19 views
11

有沒有更簡單或更好(= >維護更容易)的方式來使用Perl和Moose實例化基於傳入數據的類?如何在Perl和Moose中編寫工廠代碼?

以下代碼是我正在處理的項目中的一個精簡示例。

package FooBar; 
use Moose; 
has 'SUBCLASS' =>('isa'=>'Str',required=>'1',is=>'ro'); 
has 'MSG' =>('isa'=>'Str',required=>'1',is=>'ro'); 

sub BUILD { 
     my $self = shift; 
     my ($a)[email protected]_; 
     bless($self,$a->{SUBCLASS}) 
} 
sub Hi { 
    my $self=shift; 
    print "Hi, I'm a " . ref($self) ." and I say [". $self->MSG()."]\n"; 
} 

package Foo; 
use Moose; 
extends ("FooBar"); 

package Bar; 
use Moose; 
extends ("FooBar"); 

package main; 
use strict; 
use warnings; 

for my $line (<DATA>) { 
    my ($case,$msg)=split(/[\n\r,]\s*/,$line); 
    FooBar->new(SUBCLASS=>$case,MSG=>$msg)->Hi(); 
} 

__DATA__ 
Foo, First Case 
Bar, Second Case 

編輯:它只是讓我吃驚,這是相當多的,當你調用DBI會發生什麼。根據您傳遞的參數,它會使用完全不同的代碼,同時保持(大部分)一致的接口

回答

10

Ick。 Stevan有一個非常引人注目的說法,認爲new應該總是隻有 才能返回Class的一個實例。其他任何事情都會讓學習新系統的人感到困惑。

你可能想看看 MooseX::AbstractFactory。 如果這不會爲你工作,那麼:

package FooBar; 
use Moose; 

has [qw(SUBCLASS MSG)] => (is => 'ro', required => 1); 

sub create_instance { 
    return $self->package->new(message => $self->msg); 
} 

package FooBar::Object; 
use Moose; 

has msg => (is => 'ro', required => 1); 

sub Hi { 
    my $self = shift; 
    print "Hi, I'm a " . ref($self) ." and I say [". $self->MSG()."]\n"; 
} 

package Foo; 
use Moose; 
extends qw(FooBar::Object); 

package Bar; 
use Moose; 
extends qw(FooBar::Object); 


package main; 
or my $line (<DATA>) { 
    my ($case,$msg)=split(/[\n\r,]\s*/,$line); 
    FooBar->new(SUBCLASS=>$case,MSG=>$msg)->create_instance->Hi 
} 

__DATA__ 
Foo, First Case 
Bar, Second Case 

當然還有很多其他的方法可以實現在穆斯這個相同的概念。不知道您的域遇到的具體問題很難告訴大家,像MooseX::Traits會不會更好:

package Foo; 
use Moose; 
with qw(MooseX::Traits); 

package Bar; 
use Moose; 
with qw(MooseX::Traits); 

package Messaging; 
use Moose::Role; 

has msg => (is => 'ro', required => 1); 

sub Hi { 
    my $self = shift; 
    print "Hi, I'm a " . ref($self) ." and I say [". $self->MSG()."]\n"; 
} 

package main; 
use strict; 
Foo->with_traits('Messaging')->new(msg => 'First Case')->Hi; 

這大致是什麼其他的海報意味着有關使用基於角色的解決方案。

+0

謝謝,需要我一段時間grok :-) – lexu 2009-08-07 15:39:45

+1

**有[qw(SUBCLASS MSG)] =>(is =>'ro',required => 1); **巧妙的技巧..但「不直觀的」對於任何不熟悉perl的人來說。 – lexu 2009-08-07 15:53:29

+0

不,它不是。它是Moose API的一部分,與Perl無關。 (如果你想變得不直觀,你可以寫下「對於qw/SUBCLASS MSG /有$ _ =>(...)」,但是當然,每個人都知道這也是。) – jrockway 2009-08-07 18:14:26

5

你可以簡單地做:

$case->new(MSG => $msg)->Hi(); 

如果是比較容易或者更好是由你來決定。

+0

使用變量作爲要實例化的類** $ case-> new(..)**以這種方式感覺很奇怪。但是,是的,非常緊湊,當我的代碼相比(我拼出來** **祝福({},$ case)** 感謝您的提示! – lexu 2009-08-07 15:58:13

+1

爲什麼這很奇怪?Perl中的類名只是一個字符串Foo-> blah與「Foo」 - > blah完全一樣 – 2009-08-07 18:31:07

+1

如果工廠沒有選擇正確的子類來實例化的邏輯,那麼它的工廠就不多了,這是一個很大的優勢 – Schwern 2009-08-07 20:08:22

4

那麼,當BUILD被稱爲已創建的對象,所以我會說

sub BUILD { 
     my $self = shift; 
     return bless $self, $self->SUBCLASS; 
} 

你可能總是希望從繼承基於模型切換到您創建所需的對象基於角色模型(而不是將類傳入工廠類),然後應用通用角色。

+0

Where你會讓我學習/理解「繼承基礎」向「基於角色」的過渡嗎? – lexu 2009-08-07 13:50:09

+3

關於角色的駝鹿手冊將是一個開始http://search.cpan.org/perldoc/Moose::Manual::Roles – 2009-08-07 14:57:05

+0

@SinanÜnür:謝謝你的鏈接! – lexu 2009-08-09 15:27:14

5

只是說明對一些問題的答案:

在調用BUILD bless,或以外的任何地方的MOP內部的,是永遠不能接受的。 (如果你必須重新分配,有Class::MOP::Class->rebless_instance!)

我第二個建議不允許new返回除__PACKAGE__的實例以外的任何其他內容。如果你想要一個創建某個事物實例的方法,可以將其稱爲別的東西。例如:

class Message { 
    method new_from_string(Str $msg){ 
     my ($foo, $bar, $baz) = ($msg =~ /<...>/); # blah blah blah 
     my $class = "Message::${foo}::$baz"; 
     Class::MOP::load_class($class); 
     return $class->new(bar => $msg); 
    } 
} 

然後,當你想創建一個文本消息:

Message->new(whatever => 'you want'); 

當你想解析字符串並返回正確的消息子類:

Message->new_from_string('OH::Hello!'); 

最後,如果創建Message的實例沒有意義,那麼它不應該是一個類。它應該是一個角色。

當然,您可以用其他物體來處理建築物。只要確保這個其他對象只負責瞭解字符串格式,例如,而不是消息內部:

​​

現在你不再關心有一些「超」負責建造「子」,這我認爲是更好的設計。 (記住,MessageString擁有該做的類「消息」無需專門的電源也就是這裏的關鍵,它只是爲了解字符串化的信息負責。)

反正現在你只是:

my $data = <>; # Yup, I called it $data. Sorry, Andy Lester. 
my $parsed = MessageString->new_from_string($data); 
my $message = $parsed->as_message_object; 
$message->interact_with 

(您知道「MVC」?這與此類似)。

3

只需使用另一個工廠對象來構造該類的對象。

更簡單,更靈活,更可靠等

my $factory = Factory->new(... factory parameters ...);

my $object = $factory->new_object(... various parameters ...);

其中new_object可以解析參數和兩個數據使內從這些參數$factory和數據進行決策。

當你發現在下一步中需要依賴於對象的對象時,請查找控制框架的反轉。