观察Perl类中属性的变化
有人能提供一个代码示例吗?如何在类内部设置变量更改的观察者?我尝试了几种使用不同特性(,)和OOP框架(Moo,Mojo::Base)的方法,但都失败了 下面是我的失败代码,以便更好地理解我的任务。在本例中,每当attr1发生更改时,我都需要更新attr2 使用Mojo::Base和Scalar::Watcher:观察Perl类中属性的变化,perl,oop,watch,mojolicious,moo,Perl,Oop,Watch,Mojolicious,Moo,有人能提供一个代码示例吗?如何在类内部设置变量更改的观察者?我尝试了几种使用不同特性(,)和OOP框架(Moo,Mojo::Base)的方法,但都失败了 下面是我的失败代码,以便更好地理解我的任务。在本例中,每当attr1发生更改时,我都需要更新attr2 使用Mojo::Base和Scalar::Watcher: package Cat; use Mojo::Base -base; use Scalar::Watcher qw(when_modified); use feature 'say'
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => 1;
has 'attr2' => 2;
has 'test' => sub { # "fake" attribute for getting access to $self
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" };
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2; # attr2 is still 2, but must be 3
使用Moo和触发器:
package Cat;
use Moo;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => &update() );
has 'attr2' => ( is => 'rw', default => 1);
sub update {
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" }; # got error here: Can't call method "attr1" on an undefined value
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2;
非常感谢您的任何建议。Moo部分
此处出现错误:无法对未定义的值调用方法“attr1”
这是因为Moo希望代码引用作为has的触发器。您正在将调用的结果传递给update
。这里的&
没有提供参考,而是告诉Perl忽略更新
函数的原型。你不会想要的
相反,用\&foo
创建一个引用,不要添加括号()
。您不想调用该函数,而是想引用它
现在,一旦完成了,就不再需要Scalar::Watcher了。触发器已经做到了这一点。每次更改attr1
时都会调用它
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
};
如果现在运行整个程序,它会工作一点,但会因以下错误而崩溃:
无法通过包“3”定位对象方法“attr2”(可能您忘记加载“3”?)
这是因为attr1
返回新值,而不是对$self
的引用。所有Moo/Moose访问器都是这样工作的。而且3
不是对象,因此它没有方法attr2
# this returns 1
# |
# V
say $me->attr1(3)->attr2;
相反,这是两个调用
$me->attr1(3);
say $me->attr2;
这里有一个完整的例子
package Cat;
use Moo;
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
has 'attr2' => ( is => 'rw', default => 1 );
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
}
package main;
my $me = Cat->new;
say $me->attr2;
$me->attr1(3);
say $me->attr2;
以及输出:
1
meow
3
为什么Scalar::Watcher不能与Mojo一起工作
首先,Mojo::Base不提供触发机制。但是您实现Scalar::Watcher的方式无法工作,因为从未调用test
方法。我尝试在基于Mojo::Base的类中挂起new
,以便在总是调用它的地方进行修改时调用
从这里开始的一切都只是猜测
下面的代码片段是我尝试过的,但它不起作用。我将在下面进一步解释原因
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => '1';
has 'attr2' => 'original';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
when_modified $self->{attr1}, sub { $self->attr2('updated'); say "meow" };
return $self;
}
如您所见,这现在是new
调用的一部分。代码确实会被执行。但这没用
结果是观察者应该在那里,直到变量超出范围
如果在void上下文中调用_modified,则观察者将
在$variable的生命周期结束前处于活动状态;否则,它将返回一个
对取消器的引用,用于在取消器被禁用时取消此观察程序
垃圾收集
但实际上我们没有标量变量。如果我们试着去做
when_modified $self->foo
然后Perl在$self
上执行foo
的方法调用,并且当_modified
将获得该调用的返回值时。我还试着深入到上面这个对象的内部,但也没有成功
我的XS不够强大,无法理解正在发生的事情,但我认为它在连接魔法时遇到了一些问题。它无法处理哈希引用值。可能这就是它被称为Scalar::Watch的原因。太好了,它可以工作,非常感谢您的详细解释!如果有人会回顾Mojo::Base+Scalar::Watcher部分,我会很高兴的。)现在我正在做一些架构决策,如果可能的话,我希望避免使用Moo或Moose作为不超重的应用程序。我正在努力。我不认为你能让它起作用。而且,它很贵。Scalar::Watcher的文档甚至说您应该只在调试时使用它。用Moo代替。它很轻。除非你在树莓上运行,否则你应该没问题。”如果你运行这个,它会工作一点,但会因为这个错误而崩溃:“这部分我花了一段时间才理解,因为看起来你仍然在谈论更新
子例程,而你实际上指的是OP代码的main
包中的调用代码。@Borodin我当时很匆忙。我稍后会说得更清楚。如果您愿意,可以随意编辑。Mojo::Base的一点是,它不应该是一个通用的对象系统。它提供了简单的行为,速度非常快。我们始终支持使用Moo(se)构建您的魅力应用程序?当你想要这样的行为,那就是最好的解决方案!
when_modified $self->foo