と呼ばれる単純なMooseベースのクラスを作成しましたDocument
。このクラスには、との2つの属性がname
ありhomepage
ます。
このクラスは、属性do_something()
に基づいてさまざまなソース(Webサイトやさまざまなデータベースなど)からテキストを取得して返すというメソッドも提供する必要があります。homepage
にはまったく異なる実装がたくさんあるので、do_something()
それらを異なるパッケージ/クラスに入れたいと思います。これらの各クラスは、homepage
属性に責任があるかどうかを知る必要があります。
これまでの私のアプローチには2つの役割があります。
package Role::Fetcher;
use Moose::Role;
requires 'do_something';
has url => (
is => 'ro',
isa => 'Str'
);
package Role::Implementation;
use Moose::Role;
with 'Role::Fetcher';
requires 'responsible';
呼び出されるクラスは、一般的に使用されるメソッド(HTTP GETリクエストなど)Document::Fetcher
のデフォルトの実装を提供します。do_something()
package Document::Fetcher;
use Moose;
use LWP::UserAgent;
with 'Role::Fetcher';
has ua => (
is => 'ro',
isa => 'Object',
required => 1,
default => sub { LWP::UserAgent->new }
);
sub do_something {'called from default implementation'}
sub get {
my $r = shift->ua->get(shift);
return $r->content if $r->is_success;
# ...
}
そして、次のような方法で責任を決定する特定の実装responsible()
:
package Document::Fetcher::ImplA;
use Moose;
extends 'Document::Fetcher';
with 'Role::Implementation';
sub do_something {'called from implementation A'}
sub responsible { return 1 if shift->url =~ m#foo#; }
package Document::Fetcher::ImplB;
use Moose;
extends 'Document::Fetcher';
with 'Role::Implementation';
sub do_something {'called from implementation B'}
sub responsible { return 1 if shift->url =~ m#bar#; }
私Document
のクラスは次のようになります。
package Document;
use Moose;
has [qw/name homepage/] => (
is => 'rw',
isa => 'Str'
);
has fetcher => (
is => 'ro',
isa => 'Document::Fetcher',
required => 1,
lazy => 1,
builder => '_build_fetcher',
handles => [qw/do_something/]
);
sub _build_fetcher {
my $self = shift;
my @implementations = qw/ImplA ImplB/;
foreach my $i (@implementations) {
my $fetcher = "Document::Fetcher::$i"->new(url => $self->homepage);
return $fetcher if $fetcher->responsible();
}
return Document::Fetcher->new(url => $self->homepage);
}
現在、これは正常に機能します。次のコードを呼び出すと:
foreach my $i (qw/foo bar baz/) {
my $doc = Document->new(name => $i, homepage => "http://$i.tld/");
say $doc->name . ": " . $doc->do_something;
}
期待どおりの出力が得られます。
foo: called from implementation A
bar: called from implementation B
baz: called from default implementation
ただし、このコードには少なくとも2つの問題があります。
既知のすべての実装のリストをに保持する必要があり
_build_fetcher
ます。名前空間の下にロードされたすべてのモジュール/クラスからコードが自動的に選択する方法が好きDocument::Fetcher::
です。または、これらの種類のプラグインを「登録」するためのより良い方法があるのでしょうか。現時点では、コード全体が少し肥大化しているように見えます。私は人々が以前にこの種のプラグインシステムを書いたことがあると確信しています。MooseXには、目的の動作を提供するものがありませんか?