如何在Perl中访问monkeypatched方法的原始方法?

时间:2009-02-22 20:22:35

标签: perl monkeypatching

我正在尝试monkey patch一个Perl类:我想改变现有方法的行为。

This node on perlmonks显示了如何函数添加到现有类中。我发现这个模式也可以用来为现有函数提供一个新的实现。

但是,我想知道如何调用原始函数。

我正在寻找类似的东西:

use ExistingClass;

# TODO: Somehow rename existingFunction() to oldExistingFunction().

sub ExistingClass::existingFunction {
    my $self = shift;

    # New behavior goes here.
    $self->oldExistingFunction(@_); # Call old behavior.
    # More new behavior here.
}

7 个答案:

答案 0 :(得分:10)

Typeglob赋值

*ExistingClass::oldExistingFunction = *ExistingClass::existingFunction;

快速而肮脏。这会将所有existingFunction符号别名化为oldExistingFunction。这包括您感兴趣的子,以及可能碰巧具有相同名称的任何标量,数组,哈希,句柄。

  • 优点:不思考,它只是有效。 “快速”
  • 缺点:“脏”

Coderef赋值

*ExistingClass::oldExistingFunction = \&ExistingClass::existingFunction;
# or something using *ExistingClass::symbol{CODE}

那个只对别名别名。它仍然在软件包存储中完成,因此oldExistingFunction符号是全局可见的,可能是也可能不是你想要的。可能不是。

  • 优点:别名不会“泄漏”到其他变量类型。
  • 缺点:思考更多,打字更多。如果采用* ... {CODE}语法(我个人每天都不使用它),还有更多的想法。

Lexical coderef

my $oldFunction = \&ExistingClass::existingFunction;

使用my保留对仅对当前块/文件可见的旧函数的引用。没有你的帮助,外部代码无法掌握它。注意召唤惯例:

$self->$oldFunction(@args);
$oldFunction->($self, @args);
  • 优点:不再有可见性问题
  • 缺点:更难做对

驼鹿

jrockway's answer。它必须是正确的方式,因为不再有使用globs和/或引用的杂乱,但我不知道它足以解释它。

答案 1 :(得分:8)

您应该使用MooseClass::Method::Modifiers

在这种情况下,你可以说:

around 'some_method' => sub {
    my ($orig, $self, @args) = @_;
    # ... before original ...
    $self->$orig(@_);
    # ... after original ...
};

答案 2 :(得分:4)

除了其他答案,请查看以下模块:

我也在Mastering Perl的“动态语言”一章中讨论过这个问题。

答案 3 :(得分:2)

Memoize就是一个很好的例子。

答案 4 :(得分:2)

只需将其复制到词法变量并调用它即可。

my $existing_function_ref = \&ExistingClass::existingFunction;
*ExistingClass::existingFunction = sub { 
    my $self = shift;
    $self->go_and_do_some_stuff();
    my @returns = $existing_function_ref->( $self, @_ );
    $self->do_some_stuff_with_returns( @returns );
    return wantarray ? @returns : shift @returns;
};

如果您对OO语法感觉更好,可以创建UNIVERSAL::apply方法(或者您选择的任何基类)。

sub UNIVERSAL::apply { 
    my ( $self, $block ) = splice( @_, 0, 2 );
    unshift @_, $self;
    goto &$block;
}

这样你就可以这样称呼它:

my @returns = $self->apply( $existing_function_ref, @_ );

答案 5 :(得分:1)

对于Moose课程,您可以do what jrockway says;对于非驼鹿班,请执行以下操作:

use Class::MOP ();
use ExistingClass;

Class::MOP::Class->initialize('ExistingClass')->add_around_method_modifier(
    existingFunction => sub {
        my $orig = shift;

        # new behaviour goes here

        # call old behaviour
        my $result = $orig->(@_);

        # more new behaviour goes here
    }
);

答案 6 :(得分:-1)

作为替代品,有什么问题:

package NewClass;
use base qw/ExistingClass/;

sub existingFunction {
# ....
}

sub oldExistingFunction {
    my $self = shift;
    return $self->SUPER::existingFunction(@_);
}