gpt4 book ai didi

perl - 在 perl 中按需加载所需的包

转载 作者:行者123 更新时间:2023-12-04 11:05:51 25 4
gpt4 key购买 nike

重新措辞的问题 - 抱歉,它有点长。

例如有一个简单的包

package My;
use Moose;
use namespace::sweep;
sub cmd1 {1}
sub smd2 {2}
__PACKAGE__->meta->make_immutable;
1;

我想允许其他人扩展 My用另一种方法,如
package My::Cmd3;
use Moose;
extends 'My';
sub cmd3 {3}
1;

这允许使用“基础” My 中的方法和 My::Cmd3下一个:
use My::Cmd3;
my $obj = My::Cmd3->new();
say $obj->cmd1(); #from the base My
say $obj->cmd3(); #from the My::Cmd3;

但是这个 不是我想要的 .我不要 use My::Cmd3; ,(这里会有更多的扩展包),我要 use My; .

使用角色更好,例如:
package My;
use Moose;
with 'My::Cmd3';
sub cmd1 {1}
sub cmd2 {2}
__PACKAGE__->meta->make_immutable;
1;

package My::Cmd3;
use Moose::Role;
use namespace::autoclean;
sub cmd3 {3};
no Moose::Role;
1;

这允许我:
use My;
my $obj = My->new();
say $obj->cmd1();
say $obj->cmd3(); #from the role-package

但是当有人制作 My::Cmd4将需要更改基础 My要添加的包 with My::Cmd4 . ;(

我正在寻找一种方法,如何实现下一个:
use My;

#and load all needed packages on demand with the interface like the next
my $obj = My->new( commands => [qw(Cmd3 Cmd4)] );
#what should load the methods from the "base" My and from the wanted extensions too

say $obj->cmd1(); # from the base My package
say $obj->cmd3(); # from the "extension" package My::Cmd3
say $obj->cmd4(); # from the My::Cmd4

所以,我现在拥有的:
package My;
use Moose;
has 'commands' => (
is => 'rw',
isa => 'ArrayRef[Str]|Undef', #??
default => sub { undef },
);

# WHAT HERE?
# need something here, what loads the packages My::Names... based on the supplied "commands"
# probably the BUILD { ... } ???

sub cmd1 {1}
sub smd2 {2}
__PACKAGE__->meta->make_immutable;
1;

设计一个正确的对象层次结构是我永恒的问题.. ;(

我绝对确定这不是一个大问题,只需要一些我应该研究的指针;因此很高兴知道一些 CPAN 模块,使用这种技术是什么......

所以问题:
  • 我需要用什么来代替上面的“这里是什么?”
  • “扩展”包应该是角色? (可能这是最好的,但要确定)
  • 我应该从My 中移动“基本”命令吗?例如My::Base并将按需加载为其他 My::Something还是应该留在 My ?为什么?
  • 其他一些建议?
  • 为了允许获取方法列表(和加载的包),在 Moose 中我可以使用

  • my $obj = My->new(....);
    my @methods = $obj->meta->get_all_methods();

    这只有 Moose我不能使用更小的 Moo , 对?

    Ps:再次为这个非常长的问题感到抱歉。

    最佳答案

    这是填写您的 WHAT HERE? 的解决方案部分,扩展保留为角色。

    package My;

    use Moose;
    use Class::Load 'load_class';

    has commands => (
    is => 'ro',
    isa => 'ArrayRef',
    default => sub { [ ] },
    );

    sub BUILD {
    my ($self) = @_;

    my $namespace = __PACKAGE__;
    foreach ( @{ $self->commands } ) {
    my $role = "$namespace::$_";
    load_class $role; # load the module
    $role->meta->apply($self); # apply the role to the object
    }
    return;
    }
    ...

    笔记:
  • 您将需要在运行时加载您的角色。这类似于 require My::Role但是该模块处理在运行时加载模块的一些问题。这里我使用了Class::Load ,但存在许多替代方案,包括 Module::Load。
  • 然后您需要将角色应用到您的对象(另请参阅 this Moose Cookbook entry 作为引用)。
  • 我建议保留方法cmd1cmd2在这个基类中,除非你有理由将它们分开并按需加载它们。
  • 我使用 BUILD在 Moose 中构造后自动调用的方法。
  • 我不允许 commands成为 undef所以我不需要检查它 - 如果没有命令,那么它可以保留为空 arrayref。


  • 您还可以使用一个模块,该模块为您提供应用角色的基础设施,而无需您自己编写。这里我使用了 MooseX::Traits ,但这里再次列出了许多替代方案: https://metacpan.org/pod/Task::Moose#Traits-Roles
    package My;

    use Moose;

    with 'MooseX::Traits';
    has '+_trait_namespace' => ( default => 'My' );

    sub cmd1 {1}
    sub cmd2 {2}
    __PACKAGE__->meta->make_immutable;
    1;

    # your roles remain unchanged

    然后使用该类:
    use My;
    my $obj = My->with_traits(qw[ Cmd3 Cmd4 ])->new;
    # or my $obj = My->new_with_traits( traits => [qw( Cmd3 Cmd4 )] );

    say $obj->cmd1;
    say $obj->cmd3;
    say $obj->cmd4;

    如果您不想使用 Moose,仍然可以使用 Moo 执行以下操作:
    use Moo::Role ();
    my $class = Moo::Role->create_class_with_roles( 'My2', 'My::Cmd3', 'My::Cmd4' );
    my $obj = $class->new;
    say $obj->cmd1;
    say $obj->cmd3;
    say $obj->cmd4;

    关于perl - 在 perl 中按需加载所需的包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25401357/

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