5

ビルダーがこのジェネリック クラスのインスタンスではなく、専用の子クラスのインスタンスを返すジェネリック クラスを作成したいと考えています。

Mooseは自動オブジェクト構築を行うため、これが可能かどうか、Moose構文を使用してこの動作を持つMooseクラスを作成する方法を理解できません。

例: ユーザーは次のように尋ねます: $file = Repository->new(uri=>'sftp://blabla').... そして `Repository::_Sftp`` インスタンスが返されます

ユーザーは$file、実際のサブクラス (ポリモーフィズム) を知る必要なく、Repository インスタンスであるかのように使用します。

注:
要求されたように、私が達成しようとしていたことについてもっと明確にする必要があったかもしれません:
私のクラスの目的は、単に「隠し」リポジトリを作成することによって、新しいリポジトリ スキーム (sftp など) を追加できるようにすることです:: _Stfp クラスを作成し、リポジトリ コンストラクターにケースを追加して、url に応じて適切な特殊化されたオブジェクトをファクトリにします。リポジトリは、特殊なオブジェクトが実装するインターフェイスを提供する、仮想基本クラスのようなものです。
これはすべて、プログラムの残りの部分を変更せずに新しいリポジトリ スキームを追加するためのものです。つまり、知らず知らずのうちに、特殊化されたインスタンスをリポジトリ インスタンスであるかのように処理してしまいます。

4

2 に答える 2

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 コードを追加し、再利用する必要があるメソッドをすべて追加します。ただし、再利用を強制する場合は注意してください。Repository ロールでタグ付けされたすべてのものがそのコードを必要としていると確信していますか?そうでない場合は、コードを別の場所に配置してください。ロールを作成し、その機能を必要とするクラスにそのロールを適用します)。

作成したビルダーを使用する方法を次に示します。たとえば、ネットワークに触れたくない場合は、次のようにします。

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,
    );
}

これで完了です。

于 2010-06-08T01:02:25.623 に答える
2

いいえ(直接ではありません)。一般にMooseでは、CLASS isa Moose::Objectを呼び出すとCLASS->new、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');
于 2010-06-08T00:50:36.353 に答える