跳轉到內容

使用 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 無法做的事情... 只是說這裡只是人們希望它可以做的事情的眾多領域之一。
華夏公益教科書