跳转到内容

使用 Moose 进行编程/语法/before、after 和 around

来自维基教科书,开放的书籍,开放的世界

beforeafteraround方法修饰符。它们控制围绕实际方法调用的一系列事件,允许用户轻松执行简单的操作。两个关键字 beforeafter 只是触发代码,而 around 允许动态重写参数。

这些功能只在 Class::MOP 中有记录。

修饰符

[编辑 | 编辑源代码]
package Jerk;
use Moose;

sub fart {
	print '\me farts';
}

before 'fart' => sub {
	print '\me laughs';
};

package main;

my $m = Jerk->new;

$m->fart;
package Manners;
use Moose;

sub fart {
	print '\me farts';
}

after 'fart' => sub {
	print 'excuse me';
};

package main;

my $m = Manners->new;

$m->fart;


# return an array ref, de-referenced.
around foo => sub {
    my $sub = shift;

    my $ret = $sub->(@_);

    @{ $ret }
}

# auto-instantiate an empty array for a hash value
around foo_lookup => sub {
    my ($next, $this, $key) = @_;
    my $arrayref = $this->$next($key);

    $arrayref ||= $this->foo_hashref->{$key} = [];

    return $arrayref;
};

无限递归

[编辑 | 编辑源代码]

您可能很想在修饰符的子程序中调用修饰符所附加的函数。这很糟糕,请观察以下内容,并且永远不要这样做。

has 'foo' => ( isa => 'Int', is => 'ro' );

after 'foo' => sub {
  my $self = shift;
  say 'Got ' . $self->foo;
}

结果是一个糟糕的无限循环。

  1. ->foo()
  2. after 修饰符执行并调用 ->foo
  3. ->foo()
  4. after 修饰符执行并调用 ->foo
  5. 无限循环

执行顺序

[编辑 | 编辑源代码]

参数重写

[编辑 | 编辑源代码]

不要做的事情

[编辑 | 编辑源代码]

这是一个大多数人认为可行的示例,但实际上不行:每个人似乎都必须尝试一次的拙劣解决方案。请注意,您永远不应该这样做![1]

package Foobar;
use Data::Dumper;
use Moose;

sub method {
	my ( $self, $ref ) = @_;
	
	print Dumper [ 'method', $ref ];

};

sub modifier {
	my ( $self, $ref ) = @_;

	print Dumper [ 'modifier before', $ref ];

	$ref->[0] = 'hax3d';

	print Dumper [ 'modifier after', $ref ];
	
}

before 'method' => \&modifier;

package main;

my $m = Foobar->new;

$m->method( ['foo'] );

返回

$VAR1 = [
          'modifier before',
          [
            'foo'
          ]
        ];

$VAR1 = [
          'modifier after',
          [
            'hax3d'
          ]
        ];

$VAR1 = [
          'method',
          [
            'hax3d'
          ]
        ];


您可能认为这可行,因为您可以将修改方法添加到方法中。但是请注意,使用这种技术会在您传递非引用时产生巨大差异。

package Foobar;
use Data::Dumper;
use Moose;

sub method {
	my ( $self, $scalar ) = @_;
	
	print Dumper [ 'method', $scalar ];

};

sub modifier {
	my ( $self, $scalar ) = @_;

	print Dumper [ 'modifier before', $scalar ];

	$scalar = 'bar';

	print Dumper [ 'modifier after', $scalar ];
	
}

before 'method' => \&modifier;

package main;

my $m = Foobar->new;

$m->method( 'foo' );
$VAR1 = [
          'modifier before',
          'foo'
        ];

$VAR1 = [
          'modifier after',
          'bar'
        ];

$VAR1 = [
          'method',
          'foo'
        ];

几乎肯定不是您想要的?[2] 别担心,现在您应该了解该怎么做。

应该做的事情

[编辑 | 编辑源代码]

Moose 之道™ 很简单,使用更合适的 around。Moose 使您能够直接明确地表达您的意图。使用 around,您的修饰符将获得:(a)所需方法的名称,(b)对 self 的引用,以及(c)发送给该函数的参数。然后,您可以 $self->$sub( @args ) 如果您想调度该函数,或者完全重写或停止调用。

package Foobar;
use Data::Dumper;
use Moose;

sub method {
	my ( $self, $scalar ) = @_;
	
	print Dumper [ 'method', $scalar ];

};

sub modifier {
	my ( $sub, $self, @args ) = @_;

	print Dumper [ 'modifier', $sub, \@args ];
	$self->$sub( @args );

}

around 'method' => \&modifier;

package main;

my $m = Foobar->new;

$m->method( 'foo' );

返回以下内容

$VAR1 = [
          'modifier',
          sub { "DUMMY" },
          [
            'foo'
          ]
        ];

$VAR1 = [
          'method',
          'foo'
        ];
  1. ^ 除非您正在编写关于该怎么做和不该做的事情的全面教程。
  2. ^ 对于更精明的程序员来说,my ( $self, $scalar ) = @_; 并不是这里的罪魁祸首。直接访问 $_[1] 将导致运行时死亡。@_ 实际上是一个常量。
  3. ^ 这并不意味着 Moose 曾经做过常规 perl 无法做的事情... 只是说这里只是人们希望它可以做的事情的众多领域之一。
华夏公益教科书