如果我可以动态创建包,我会遇到一种情况。下面的代码不会运行,但说明了我想做的事情的精神。
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $basename = "Xma";
my $len = 7;
my $newClass = sprintf("%s%d", $basename, $len);
printf("New class is %s\n", $newClass);
package $newClass {
# Early modules.
our @enum; BEGIN { @enum = qw( I_VAL I_SLOPE ); }
use parent qw(Exporter);
use enum::fields @enum;
our @EXPORT = (@enum);
our $classLen = $len;
our $classBasename = $basename;
sub new {
my $invocant = shift;
my $self = bless ([], ref $invocant || $invocant);
return($self);
}
}
1;
我在我的新类中包含了“额外”的东西,以说明新类是一个复杂的类,它将在继承链中。
我知道我可以用类似的东西做简单的包:
*{ "${class}::new" } = sub { return bless { }, $class };
但是我的新类/包会很大,所以我希望有一些更容易维护的东西。
你们中间的好奇者可能会问,“为什么?”好奇心很大。我需要在加载/编译时尽可能多地处理进程,并试图避免数百万运行时查找。我要换
$basename
和 $len
经常,但只在编译时。还有最重要的一点:好奇心。
最佳答案
我不想成为那个说它的人,但可变代码(?!)是 反模式对于我能想到的大多数用例,除了明显的代码生成工具或转译器之外,还有一个很大的危险信号。
最好只创建一个类或类层次结构,然后使用非元编程处理该类中的任何差异。
事实上,这就是 OOP 的总体思路。您创建的对象是您的类的"template"的实例或副本:
package MyClass {
sub new {
my ( $class, %args ) = @_;
bless { name => sprintf('%s%d', $args{basename}, $args{len}) }, $class;
}
}
my $obj = MyClass->new( basename => 'Xma', len => 7 );
printf "New class is %s\n", $obj->{ name };
如果你真的需要动态创建包/类,我能想到的最明智的方法是动态创建从一个或多个基类继承的类。
package MyClass {
sub new {
my ( $class, %args ) = @_;
bless \%args, $class;
}
sub foo {
my $self = shift;
return "instance of $self";
}
}
sub build_class {
my ( %args ) = @_;
my $classname = sprintf( '%s::%s%d', $args{ parent }, $args{ basename }, $args{ len } );
@{ "${classname}::ISA" } = ( $args{ parent } );
return $classname;
}
my $classname = build_class( parent => 'MyClass', basename => 'Xma', len => 7 );
my $obj = $classname->new();
printf "New class is %s\n", ref $obj; # is MyClass::Xma7
printf "Object is an %s\n", $obj->foo; # a MyClass::Xma7=HASH(...) object instance
如需更多面向 OOP 的元编程能力,请查看
Moose::Meta::Class
或 Class::MOP
:use Class::MOP;
my $basename = 'Xma';
my $len = 7;
my $classname = "MyClass::${basename}${len}";
Class::MOP::Class->create(
$classname,
attributes => [
Class::MOP::Attribute->new( 'foo', is => 'rw', isa => 'Str' )
],
methods => {}
);
$classname->new( foo => 'bar' );
另一方面,如果你真的需要在你的包中包含可变代码和/或你不需要/不想在每次程序运行时在运行时将所有包加载到内存中,我会说你可以使用 一组文件模板和一些模板语言您选择将新包输出到文件(即“MyClass/Xma7.pm”、“MyClass/Xma8.pm”)然后使用
use MyClasses::Xma7;
加载它们就像任何其他包一样。这里的优点是,考虑到元编程的错误倾向,您的代码将更容易调试和测试。
关于perl - 如何在 perl 中动态创建包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57385262/