在perl中向现有对象添加新方法

Roo*_*ter 5 oop perl

我有这个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;

}
Run Code Online (Sandbox Code Playgroud)

编辑:

我需要这样做的原因是我在我的代码中添加一个钩子,我的软件用户将能够注入自己的子来编辑数据结构.

为此,他们将能够编辑一个只包含一个sub的辅助文件,并获取传入的数据结构,如下所示:

sub inject_a_sub {
    my ($self, $rec) = @_;

    #do stuff to $rec

    return $rec;
}
Run Code Online (Sandbox Code Playgroud)

然后在我的原始对象实例化后,我检查上面提到的文件是否存在,如果是这样,请阅读其内容并评估它们.最后,我想制作eval'd代码,它只是一个sub,一个我的对象的方法.确切地说,我的对象已经继承了一个被调用的方法,do_something并且我希望通过eval使子读取覆盖do_something被继承的方法,以便在调用时从外部文件运行sub.

这是一个奇怪的问题:/

它伤害了我:(

Obi wan kenobi你是我唯一的希望!

干杯!

Sch*_*ern 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);
Run Code Online (Sandbox Code Playgroud)

问题是,您需要检查$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 );
Run Code Online (Sandbox Code Playgroud)

如果您已经知道要将方法注入的类名,请正常编写子例程但使用完全限定名称.

sub Some::Class::new_method {
    my $self = shift;

    ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,该子例程中的任何全局变量都将位于周围的包中,而不是在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 { ... });
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

我通过检查它的类是否匹配来省略代码以检查实例是否已经有这种处理/^${instance_class}::/.我把它留作练习给你.为每个对象创建一个新类并不便宜,并且会花费内存.


有充分的理由这样做,但它们是特殊的.你应该真的,真的质疑你是否应该做这种猴子修补.通常,应避免远距离动作.

你能用子类,委托还是角色完成同样的事情?

已经存在Perl OO系统,它将为您提供更多功能.你应该使用一个. Moose,Moo(通过Role :: Tiny)和Mouse都可以为实例添加角色.

  • @Rooster正如我在答案中指出的那样,这是一个象征性的参考.`strict`不允许他们(有充分的理由)你必须暂时改变这个限制."一个人告诉我做的事情"并不是一个很好的理由,我不在乎他们已经多少年了,如果你不理解你可能不应该弄乱它的理由.这是可能导致更多问题的"解决方案"之一.问一个关于原始问题的问题怎么样?如果你在评论中链接它,我会看看. (2认同)
  • @Rooster Re.*"我必须坚信,这是一个特殊情况之一,因为一个15年的perl家伙告诉我这样做"*Stack Overflow的好处是有很多"15年perl伙计们"(甚至可能是16,17,18岁的perl家伙),他们毫不犹豫地指出对方的错误.当你有一大群人可以提供帮助时,你不必只听一个人. (2认同)