我编写了一个基于Moose 的简单类,名为
此类还需要提供一个名为
由于
到目前为止,我的方法涉及两个角色:
一个名为
Document
。该类有两个属性:name
和homepage
。此类还需要提供一个名为
do_something()
的方法,根据homepage
属性从不同来源(如网站或不同数据库)检索并返回文本。由于
do_something()
将有很多完全不同的实现,我希望它们分别在不同的包/类中,并且每个类都应该知道它是否负责homepage
属性。到目前为止,我的方法涉及两个角色:
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';
一个名为
Document::Fetcher
的类,它提供了do_something()
的默认实现和常用方法(例如HTTP GET请求):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
但是这段代码至少存在两个问题:
我需要在
_build_fetcher
中保留所有已知实现的列表。我更希望一种方式,让代码自动从命名空间Document::Fetcher::
下加载的每个模块/类中选择。或者也许有一种更好的方法来“注册”这些插件?目前整个代码看起来有点臃肿。我相信人们之前已经编写过这种插件系统。MooseX中没有提供所需行为的内容吗?
%Document :: Fetcher ::
。 - Sebastian Stumpffetchers
的ArrayRef[Str]
属性。因此,我只需调用__PACKAGE__->meta->fetchers->add
即可。 :-) - Sebastian Stumpf