gpt4 book ai didi

Perl/Moose - 如何动态选择方法的特定实现?

转载 作者:行者123 更新时间:2023-12-04 14:48:20 26 4
gpt4 key购买 nike

我写了一个简单的Moose基于类称为 Document .这个类有两个属性:namehomepage .

该类还需要提供一个名为 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 中没有什么吗?哪个提供了所需的行为?
  • 最佳答案

    您正在寻找的是 Factory ,特别是 Abstract Factory . Factory 类的构造函数将根据其参数确定要返回的实现。

    # Returns Document::Fetcher::ImplA or Document::Fetcher::ImplB or ...
    my $fetcher = Document::Fetcher::Factory->new( url => $url );
    _build_fetcher 中的逻辑将进入 Document::Fetcher::Factory->new .这会将 Fetchers 与您的文档分开。与 Document 知道如何确定它需要哪个 Fetcher 实现不同,Fetchers 可以自己完成。

    如果您的首要任务是允许人们添加新的 Fetcher 而无需更改 Factory,那么您让 Fetcher 角色能够通知 Factory 是否能够处理它的基本模式是好的。不利的一面是,Fetcher::Factory 无法知道多个 Fe​​tcher 可能对给定的 URL 有效,并且一个可能比另一个更好。

    为了避免在 Fetcher::Factory 中硬编码一大堆 Fetcher 实现,让每个 Fetcher 角色在加载时向 Fetcher::Factory 注册自己。
    my %Registered_Classes;

    sub register_class {
    my $class = shift;
    my $registeree = shift;

    $Registered_Classes{$registeree}++;

    return;
    }

    sub registered_classes {
    return \%Registered_Classes;
    }

    如果你想要你的蛋糕并吃掉它,你可以有一些东西,可能是 Document,预加载一堆常见的 Fetchers。

    关于Perl/Moose - 如何动态选择方法的特定实现?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10954827/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com