2009-06-16 58 views
3

我正在尋找以下模式。 (我正在使用Perl,但我認爲語言並不重要)。如何創建派生類由創建屬性隱式指定的對象?

與父母班Foo,和兒童酒吧,巴茲,Bazza。

構建Foo的方法之一是解析一個字符串,並且該字符串的一部分將隱式指定要創建哪個類。例如,如果它啓動'http:',那麼它是一個Bar,但如果它不包含'[Date]',那麼Baz喜歡它,等等。

現在,如果Foo知道它的所有孩子,以及什麼字符串是一個Bar,什麼是Baz等,它可以調用適當的構造函數。但是基礎班不應該有任何關於其子女的知識。

我想要的是Foo的構造函數能夠依次嘗試它的孩子,直到其中一個人說:「是的,這是我的,我會創造出這個東西」。

我知道,在一般情況下,這個問題沒有明確定義,因爲可能有多個孩子接受這個字符串,所以他們被稱爲重要的順序:忽略這個,並假設字符串的特徵是隻有一個子類會喜歡字符串。

我想出的最好的方法是讓子類在初始化時對基類進行「註冊」,以便它獲取構造函數列表,然後遍歷它們。但有沒有更好的方法,我錯過了?

示例代碼:

package Foo; 

my @children; 

sub _registerChild 
{ 
    push @children, shift(); 
} 

sub newFromString 
{ 
    my $string = shift; 
    foreach (@children) { 
    my $object = $_->newFromString(@_) and return $object; 
    } 
    return undef; 
} 

package Bar; 
our @ISA = ('Foo'); 

Foo::_registerChild(__PACKAGE__); 

sub newFromString 
{ 
    my $string = shift; 
    if ($string =~ /^http:/i) { 
    return bless(...); 
    } 
    return undef; 
} 

回答

5

也許你可以用Module::Pluggable來實現這個?這將消除註冊的需要。

我之前採用的方法是使用Module :: Pluggable加載我的子模塊(這使我可以通過簡單地編寫和安裝它們來添加新的子模塊)。每個子類都有一個構造函數,可以返回一個有福的對象或undef。你循環你的插件,直到你得到一個對象,然後返回它。

喜歡的東西:

package MyClass; 
use Module::Pluggable; 

sub new 
{ 
    my ($class, @args) = @_; 
    for my $plugin ($class->plugins) 
    { 
     my $object = $plugin->new(@args); 
     return $object if $object; 
    } 
} 

Class:Factory很好,但可能是有點潔癖您的需求。

+0

謝謝。邏輯模塊:: Pluggable非常好,正是我所期待的;但它可能不適合我們的情況,因爲它可能不會出現在我們用戶的機器上,所以我們必須使用我們自己的模塊進行分發。 與我的註冊解決方案相比,這也是「神奇的」,所以它可能不太清楚。 但是,感謝您的關注。 – 2009-06-16 14:27:48

+0

這是有點神奇,但默認行爲只會加載MyClass :: Plugin命名空間中的類(並且該命名空間很容易覆蓋)。因此,註冊與在該名稱空間中創建模塊的效果完全相同。 很顯然,它是一個CPAN模塊,但如果您現在可以在沒有CPAN(或大量的車輪重新創建)的情況下管理Perl中的任何嚴重應用程序,我會感到驚訝。 – 2009-06-16 15:06:05

0

您可以實現類Foo一個任意查找算法,即搜索現有的子類。也許基於子類提供的配置文件,或者你可能想到的任何其他機制。

然後Foo類會在運行時檢測現有的客戶類並依次調用它們。

另外,您可以緩存查找結果並接近您已經描述過的註冊表解決方案。

+0

謝謝。在我看來,如果我必須這樣做,那麼我的「註冊」就更簡單,更清晰。我希望有一個我已經錯過的既定模式。 – 2009-06-16 11:56:29

+0

決定歸結於您想要解決的用例。如果你想讓你的Foo工廠開放給未知的第三方兒童班,那麼你會做一個查詢。 如果你生活在一個受控的環境中,只支持你自己的代碼,那麼註冊表就沒事了。 – mkoeller 2009-06-17 13:55:15

0

如果您對您的父類的評論不包含有關chilidren的信息以及您將方法委託給類自身的子類適合性的任務,那麼從父類中選擇類選擇可能是正確的併爲此任務創建一個單例。

至少這將是我的偏好...從這個當前的父類(假設在你的子類中有一些共同的功能)可能會變成抽象的或者是一個接口。

然後,單身人士可以管理所有子類的構建及其分佈(如果它們不起作用克隆它們)......此外,子類可以移入單獨的dll以促進分離。

對不起,這不是一個直接的解決方案。 我在過去通過管理單例中的類列表,就像你在這裏一樣。單身人士背後的想法是,如果你想使用任何昂貴的反射,你只需要做一次。

1

看來你試圖讓一個類既是基類又是工廠。別。使用2個獨立的課程。事情是這樣的:

package Foo; 

package Bar; 
use base 'Foo'; 

package Baz; 
use base 'Foo'; 

package Bazza; 
use base 'Foo'; 

package Factory; 
use Bar; 
use Baz; 
use Bazza; 

sub get_foo { 
    my ($class, $string) = @_; 
    return Bar->try($string) || Baz->try($string) || Bazza->try($string); 
} 

,然後用它喜歡:

my $foo = Factory->get_foo($string); 

這樣的基礎類並不需要了解你的子類,只有你的工廠做。而且兒童班也不需要彼此瞭解,只有工廠需要了解哪些兒童班的詳細信息以及按照哪個順序進行。