我有这个perl对象。在实例化对象之后,我正在尝试在loader方法中向对象添加一个新方法,然后可以稍后调用它。
我尝试了一大堆没有用的东西。例子包括:
sub loader {
my ($self) = @_;
sub add_me {
my ($self, $rec) = @_
warn "yayyyyyy";
return $rec;
}
#here are the things I've tried that dont work:
# &{$self->{add_me}} = \&add_me;
# \&{$self->{add_me}} = \&add_me;
# assuming the class definition is in Holder::Class try to add it to symblol table
# *{Holder::Class::add_me} = \&add_me;
}
修改
我需要这样做的原因是我在我的代码中添加了一个钩子,我的软件用户将能够注入自己的子来编辑数据结构。
为此,他们将能够编辑一个只包含一个sub的辅助文件,并获取传入的数据结构,如下所示:
sub inject_a_sub {
my ($self, $rec) = @_;
#do stuff to $rec
return $rec;
}
然后在我的原始对象实例化之后,我检查上面提到的文件是否存在,如果是这样,请阅读其内容并评估它们。最后,我想制作eval'd代码,它只是一个sub,一个我的对象的方法。确切地说,我的对象已经继承了一个名为do_something
的方法,并且我希望通过eval使子读取覆盖继承的do_something
方法,以便在调用时从外部文件运行sub
这是一个奇怪的问题:/
它伤害了我:(
Obi wan kenobi你是我唯一的希望!
干杯!
答案 0 :(得分:9)
如果您只想将功能附加到特定对象,并且不需要继承,则可以在对象中存储代码引用并调用它。
# Store the code in the object, putting it in its own
# nested hash to reduce the chance of collisions.
$obj->{__actions}{something} = sub { ... };
# Run the code
my @stuff = $obj->{__actions}{something}->(@args);
问题是,您需要检查$obj->{__actions}{something}
是否包含代码参考。我建议的是围绕这个过程包装一个方法。
sub add_action {
my($self, $action, $code) = @_;
$self->{__actions}{$action} = $code;
return;
}
sub take_action {
my($self, $action, $args) = @_;
my $code = $self->{__actions}{$action};
return if !$code or ref $code ne 'CODE';
return $code->(@$args);
}
$obj->add_action( "something", sub { ... } );
$obj->take_action( "something", \@args );
如果您已经知道要将方法注入的类名,请正常编写子例程,但使用完全限定名称。
sub Some::Class::new_method {
my $self = shift;
...
}
请注意,该子例程中的任何全局变量都将位于周围的包中,而在Some :: Class中不是。如果您想要持久变量,请在子例程内使用state
或在子例程外使用my
。
如果你在编译时不知道这个名字,你就必须将子程序注入到符号表中,所以你接近最后一个。
sub inject_method {
my($object, $method_name, $code_ref) = @_;
# Get the class of the object
my $class = ref $object;
{
# We need to use symbolic references.
no strict 'refs';
# Shove the code reference into the class' symbol table.
*{$class.'::'.$method_name} = $code_ref;
}
return;
}
inject_method($obj, "new_method", sub { ... });
Perl中的方法与类相关联,而不是与对象相关联。为了将方法分配给单个对象,您必须将该对象放入其自己的类中。与上面类似,但您必须为每个实例创建一个子类。
my $instance_class = "_SPECIAL_INSTANCE_CLASS_";
my $instance_class_increment = "AAAAAAAAAAAAAAAAAA";
sub inject_method_into_instance {
my($object, $method_name, $code_ref) = @_;
# Get the class of the object
my $old_class = ref $object;
# Get the special instance class and increment it.
# Yes, incrementing works on strings.
my $new_class = $instance_class . '::' . $instance_class_increment++;
{
# We need to use symbolic references.
no strict 'refs';
# Create its own subclass
@{$new_class.'::ISA'} = ($old_class);
# Shove the code reference into the class' symbol table.
*{$new_class.'::'.$method_name} = $code_ref;
# Rebless the object to its own subclass
bless $object, $new_class;
}
return;
}
我省略了代码以检查实例是否已经通过检查其类是否匹配/^${instance_class}::/
来进行此处理。我把它留作练习给你。为每个对象创建一个新类并不便宜,并且会耗费内存。
有充分的理由这样做,但它们是例外。你应该真的,真的质疑你是否应该这样做monkey patching。通常,应避免使用action at a distance。
您可以使用子类,委托或角色完成相同的事情吗?
已经存在Perl OO系统,它将为您提供更多功能。你应该使用一个。 Moose,Moo(通过Role::Tiny)和Mouse都可以为实例添加角色。