2009-06-16 19 views
3

私は次のパターンを探しています。 (私はPerlで働いていますが、特に言語が重要とは思わない)。派生クラスが作成プロパティによって暗黙的に指定されているオブジェクトを作成するにはどうすればよいですか?

親クラスFooと子Bar、Baz、Bazzaを使用します。

Fooを構築する方法の1つは、文字列を解析することであり、その文字列の一部は、作成するクラスを暗黙指定します。たとえば、 'http:'を起動するとBarですが、それには '[Date]'が含まれていればBazはそれを好きです。

Fooがすべての子を知っていて、どの文字列がBarであるか、Bazなどは、適切なコンストラクタを呼び出すことができます。しかし、基本クラスはその子どもについての知識を持っていてはなりません。

私が望むのは、Fooのコンストラクタが子供たちを順番に試すことができることです。そのうちの1人が「はい、これは私です。私はその物を作成します」と言うまでです。

一般的なケースでは、この問題は明確に定義されていません。文字列を受け入れる複数の子が存在する可能性があります。したがって、それらは問題と呼ばれる順序です。これを無視して、文字列の特性は、文字列が好きな子クラスが1つだけであるようなものです。

ベストは、子クラスが初期化時に基本クラスに「登録」して、コンストラクタのリストを取得し、それらをループすることです。しかし、私が欠けているより良い方法がありますか?

サンプルコード:

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

ありがとうございます。 Logical Module :: Pluggableは、まさに私が探していたものです。ユーザーのマシンには存在しない可能性があるため、私たちの状況には理想的ではないかもしれないので、独自のモジュールで配布する必要があります。 登録ソリューションに比べて「魔法」なので、あまり明確ではないでしょう。 しかし、それを私の注意を引くことに感謝します。 –

+0

これは魔法のようなものですが、デフォルトの動作ではMyClass :: Pluginネームスペースでのみクラスがロードされます(ネームスペースは簡単にオーバーライドできます)。したがって、登録はその名前空間にモジュールを作成することと事実上同じです。 明らかに、CPANモジュールですが、最近CPANを使わずにPerlで重大なアプリケーションを管理できれば驚くでしょう。 –

0

既存の子クラスを検索Fooクラス内の任意の検索アルゴリズムを実装することができます。たぶん、子クラスやその他の考えられるメカニズムで提供される設定ファイルに基づいているかもしれません。

クラスFooは、実行時に既存のクライアントクラスを検出し、順番に呼び出します。

さらに、ルックアップ結果をキャッシュして、すでに説明したレジストリソリューションに近づけることができます。

+0

ありがとうございました。そんなことをしなければならないのなら、私の「登録」はもっとシンプルで分かりやすくなっているようです。私は欠けていた確立されたパターンがあることを望んでいた。 –

+0

あなたが取り組んでいるユースケースが決定します。あなたが未知の第三者の子クラスにFooの工場を開いたままにしたい場合は、検索を行います。 あなたが管理している環境に住んでいて、あなた自身のコードだけをサポートしているなら、レジストリはうまくいくでしょう。 – mkoeller

0

chilidrenに関する情報が含まれていない親クラスについてのコメントや、子クラスの適合性をクラス自体に委譲する方法を記載している場合は、親からのクラス選択を除外するのが正しいでしょうこのタスクのためのシングルトンを作成します。

少なくとも私の好みになります...これからあなたの現在の親クラス(おそらく、あなたの子クラスに共通の機能をいくつか持っているでしょう)は、おそらくは抽象クラスまたはインターフェースになります。

シングルトンは、すべての子クラスとそれらの配布の構造を管理することができます(機能していない場合はそれらを複製します)...さらに、子クラスを別のdllに移動して分離を促進できます。

申し訳ありませんが、それは直接的な解決策ではありません。 私はあなたがここにいるのと同じように、シングルトンのクラスのリストを管理することでこれまでこれを行ってきました。シングルトンの背後にあるアイデアは、高価な反射を使用したい場合は、一度やり直さなければならないということです。

+0

ありがとうございます - それはシングルトンではなく、抽象基本クラスです。オブジェクトが作成されると、多態性によって正しいメソッドが得られます。それは、私が心配していたサブクラス化されていないオブジェクトがない場合の処理​​方法です。 –

1

あなたは、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); 

あなたのベースクラスは、あなたの子クラスについて知っている必要はありません。この方法で、あなただけの工場がありません。また、子クラスはお互いを知る必要はなく、どの子クラスを試し、どの順番でどの詳細を知る必要があります。

+0

このアプローチでは、子クラスの完全なセットについて知る必要があるコード内の場所さえあります。レジストリソリューションよりもさらに悪いと思う。 子クラスを追加/削除する場合は、他のコードが一貫して変更されているコードを用意する必要があります。だからあなたのアイデアは、パターンが通常目指しているように、変化に柔軟性を与えるものではありません。 – mkoeller

+0

@mkoellerでは、特定の名前空間ala Module :: Pluggableにクラスのセットを自動的にロードするのは簡単です。私は驚きを避けるためにこれらの種類のことを明示することを一般的に好みますが、それは変化に対する柔軟性よりも味の問題です。柔軟性は、この文字列を処理できる新しいサブクラスを簡単に追加できることを意味します。ファイルシステム上に存在する場合、自動的にそれらを自動で処理することを意味する必要はありません。 – mpeters

関連する問題