2010-06-08 53 views
5

我想創建一個泛型類,其構建器不會返回此泛型類的實例,而是一個專用子類的實例。由於Moose會自動構建對象,所以我不明白這是否可行,以及如何使用Moose語法創建一個Moose類並具有此行爲。如何讓Moose返回一個子類實例,而不是它自己的類,用於多態性

如: 用戶問:$file = Repository->new(uri=>'sftp://blabla') ....並返回一個'庫:: _ Sftp``例如

用戶將使用$file,就好像它是一個存儲庫實例,而不需要知道實子類(多態性)

注:
按照要求,也許我應該是什麼,我試圖實現更清晰:
我的課的目的是爲了能夠添加新的存儲庫方案(例如,超過sftp),只需創建一個「隱藏的」Repository :: _ St​​fp類,然後添加一個在Repository構造函數中根據url來生成正確的專用對象。存儲庫就像一個虛擬的基類,提供了專門的對象將實現的接口。
所有這些都是爲了添加新的存儲庫方案,而不需要修改程序的其餘部分:它會在不知不覺中處理專用實例,就好像它是一個存儲庫實例。

回答

6

new建造建造者。你需要一些其他方法來實際返回構建的對象。

下面是一個例子:

class RepositoryBuilder { 
    has 'allow_network_repositories' => (
     is  => 'ro', 
     isa  => 'Bool', 
     required => 1, 
    ); 

    method build_repository(Uri $url) { 
     confess 'network access is not allowed' 
      if $url->is_network_url && !$self->allow_network_repositories; 

     my $class = $self->determine_class_for($url); # Repository::Whatever 
     return $class->new(url => $url); 
    } 
    } 

    role Repository { <whatever } 

    class Repository::File with Repository {} 
    class Repository::HTTP with Repository {} 

這裏,建設者和內置的對象是不同的。該構建器是一個真實對象,帶有參數,可根據具體情況進行自定義以構建 對象。然後,「構建」的對象是 只是返回一個方法的值。這使您可以根據具體情況構建其他 構建器。 (構建器 函數的一個問題是它們非常不靈活 - 很難教它們 一個新的特例。構建器對象 仍存在此問題,但至少您的應用程序可以創建子類,實例化它, 而這個對象傳遞給任何需要創建對象,但 依賴注入是在這種情況下,更好的方法。)

而且,也沒有必要爲您打造從 任何繼承庫,他們只需要一個標記表明它們是存儲庫。 這就是我們的Repository角色所做的。 (您會希望在此添加 API代碼以及任何應該重用的方法。但要小心 關於強制重用 - 你確定所有用 存儲庫角色標記的東西都需要這樣的代碼嗎?如果沒有,只需將代碼放入 中,然後將其應用於需要 功能的類。)

以下是我們如何使用我們創建的構建器。比如說,如果你不想 觸控網絡:

my $b = RepositoryBuilder->new(allow_network_repositories => 0); 
$b->build_repository('http://google.com/'); # error 
$b->build_repository('file:///home/whatever'); # returns a Repository::Foo 

但是,如果你這樣做:

my $b = RepositoryBuilder->new(allow_network_repositories => 1); 
$b->build_repository('http://google.com/'); # Repository::HTTP 

現在你有一個建立的對象,你喜歡的方式建設者, 你只需要在其他代碼中使用這些對象。因此,拼圖中的最後一塊 指代其他 代碼中的「任何」類型的Repository對象。這很簡單,你用does代替isa

class SomethingThatHasARepository { 
    has 'repository' => (
     is  => 'ro', 
     does  => 'Repository', 
     required => 1, 
    ); 
} 

大功告成。

+0

mmh ...讓我明白這個角色的事情,回到穆斯文檔... 另外,我首先想到它,因爲我剛開始與穆斯,是否繼續做不好的駝鹿不是駝鹿 – 2010-06-08 11:18:09

+0

@alex:如果你按照Moose :: Manual :: *中的例子,你可以使用駝鹿的大部分特徵而不會遇到困難。只有當你開始使用'meta'時,事情纔會變得瘋狂:) – Ether 2010-06-08 16:04:33

+0

ok,理解,但我很好奇這些細微的差異: a)一個包含所有基本屬性的Moose基類「Repository」專門的Moose類'Repository :: _Sftp'表示「擴展」/繼承基類;一個駝鹿(或不)工廠'RepositoryBuilder',用一種方法爲我創建一個專門類的實例,它可以作爲'Repository'被無意識地操縱。 b)和a)一樣,但是用一個角色代替基類< - 這就是你的建議,對吧? c)和d)MooseX :: AbstractFactory或MooseX :: ABC,但這些默認情況下不可用(我把centos5作爲ref) – 2010-06-09 04:26:23

2

否(不直接)。一般在Moose中,調用CLASS->new其中CLASS isa Moose :: Object將返回一個CLASS的實例。

你能更詳細地描述你想要達到的目標嗎?爲什麼你認爲這是你想要的?您可能想要構建一個工廠類 - 當您調用其上的方法時,它將調用相應的類的構造函數並將該對象返回給您,而不必關心返回的特定類型:

package MyApp::Factory::Repository; 

sub getFactory 
{ 
    my ($class, %attrs); 

    # figure out what the caller wants, and decide what type to return 
    $class ||= 'Repository::_Sftp'; 
    return $class->new(attr1 => 'foo', attr2 => 'bar', %attrs); 
} 

my $file = MyApp::Factory::Repository->getFactory(uri=>'sftp://blabla'); 
+0

這是我以前有過的,我也用Class :: Accessor。有人在昨天問我關於C的問題:A提到我應該嘗試Moose(答案與本案無關)。所以這就是爲什麼我試圖「ossos」我的工廠。 至於目的,我添加了一個筆記,主要問題 – 2010-06-08 11:15:26