perl 如何在构造对象时不执行触发器

woobm2wo  于 2022-11-15  发布在  Perl
关注(0)|答案(3)|浏览(117)

我有这样的代码:

package Foo;
use Moo;

has attr => ( is => "rw", trigger => 1 );

sub _trigger_attr 
    { print "trigger! value:". shift->attr ."\n" }

package main;
use Foo;

my $foo = Foo->new( attr => 1 );
$foo->attr( 2 );

它会传回:

$ perl test.pl
trigger! value:1
trigger! value:2

这是Moo中触发器的默认记录行为。
如果属性是通过构造函数设置的,如何禁用触发器执行?
我当然可以这样做:

package Foo;
use Moo;

has attr        => ( is => "rw", trigger => 1 );
has useTriggers => ( is => "rw", default => 0 );

sub _trigger_attr 
{ 
    my $self = shift;
    print "trigger! value:". $self->attr ."\n" if $self->useTriggers 
}

package main;
use Foo;

my $foo = Foo->new( attr => 1 );
$foo->useTriggers( 1 );
$foo->attr( 2 );

并得到:

$ perl testt.pl
trigger! value:2

所以它的工作,但......这感觉不对;).

qco9c6ql

qco9c6ql1#

我不太了解Moo,但是在Moose中,你可以在构造函数之后实现你自己的代码。如果你能在Moo中这样做,它会给予你想要的效果。

sub BUILD {
    my $self = shift;

    # Sets "useTriggers" AFTER the object is already constructed.
    $self->useTriggers(1);
};

这将导致useTriggers在构造之后立即被设置,因此触发器将在构造对象之后而不是在构造对象之前处于活动状态。
因此,您应该可以写入:

my $foo->new(attr => 1);
$foo->attr(2);

并获得相同的输出。

e0bqpujr

e0bqpujr2#

package Foo;
use Moo;

has attr => ( accessor => '_attr' );

sub attr { 
    my $self = shift;
    my $rv = $self->_attr(@_);
    print "trigger! value: ", $rv if @_;
    return $rv;
}

package main;
use Foo;

my $foo = Foo->new( attr => 1 );
$foo->attr( 2 );
ffdz8vbo

ffdz8vbo3#

因为Perl 5.36发布时签名不再是实验性的,所以Moose生态系统中的事情变得复杂了。
对于Moo,没有任何更改,useTriggers技巧或构建自定义访问器仍然是有效答案。
对于MooseMouse,当通过属性访问器更改值时,也会传递旧值。因此,从构造函数调用的触发器和在现有属性上调用的触发器可以通过arity进行区分。常见模式为:

sub _change_attribute {
    my ( $self, $new ) = @_;

    return unless int @_ == 3;    # no old value, we're in constructor

    do something...

使用签名有一个新的问题-如果使用了签名,就无法检查@_(这被认为是实验性的,会抛出一个警告,通常不鼓励这样做)。因此,当将签名适配到变量触发器子元数时,可能会陷入这个陷阱:

sub _change_attribute ( $self, $new, $old = undef ) {

    return unless defined $old;

    do something...
}

当属性本身不能为Undef时,这才起作用否则,触发器将无法按预期在

  • $object->attribute( undef );
  • 哇哦!

改变。
这个笨拙的解决方案是故意将旧的param错误键入为slurpy array

sub _change_attribute ( $self, $new, @old ) {

    return unless @old;   # no old value, we're in constructor

    do something...
}

当通过属性访问器更改值时(属性是否可以为Undef无关紧要),它将正确激发,并且当从构造函数初始化时将激发并跳过逻辑。

相关问题