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,
);
}
これで完了です。