我有一个Moo(se)[0]类,其中包含许多方法,这些方法在顶部具有完全相同类型的“guard语句”。我没有多次编写相同的代码,而是认为我可以将语句放在“before”方法修饰符中,并且完美无缺。除非这个类是子类,否则永远不会调用“前卫”。
package Foo;
use feature 'say';
use Moose;
has '_init' => (
is => 'rw',
isa => 'Bool',
default => 0
);
sub init {
shift->_init(1);
}
sub method {
say "in Foo::method";
}
before method => sub {
my $self = shift;
warn "==> Foo is not initialized\n" unless $self->_init;
};
package Bar;
use feature 'say';
use Moose;
extends 'Foo';
sub method {
say "in Bar::method";
}
package main;
use feature 'say';
my $foo = Foo->new;
say "foo the wrong way:";
$foo->method;
say "foo the right way:";
$foo->init;
$foo->method;
my $bar = Bar->new;
say "bar the wrong way:";
$bar->method;
然后输出(添加一些新行):
foo the wrong way:
==> Foo is not initialized
in Foo::method
foo the right way:
in Foo::method
bar the wrong way:
in Bar::method
我认为这种行为是设计的,但是有没有(好的)方法来确保所有子类也继承“before”方法修饰符/ guard语句?或者有不同的方法来实现这一点(我怀疑这是一个相当常见的结构)。请注意,真正的guard语句中会抛出异常,但在示例代码中“警告”更简单。
[0]我更喜欢使用Moo,因为我没有使用任何需要MOP的功能,但是Moo和Moose在这个问题上的工作方式完全相同。
使用角色修改。
如果为此添加Role
(由tobyink建议),并添加另一种方法使事情变得更加“真实”,我会得到一个奇特的结果。
package Warning::NotInit;
use feature 'say';
use Moose::Role;
has '_init' => (is => 'rw', isa => 'Bool', default => 0);
before qw/ m1 m2 / => sub {
my $self = shift;
my $class = ref($self);
warn "==> $class is not initialized\n" unless $self->_init;
};
package Foo;
use feature 'say';
use Moose;
with 'Warning::NotInit';
sub init { shift->_init(1) }
sub m1 { say "in Foo::m1" }
sub m2 { say "in Foo::m2" }
package Bar;
use feature 'say';
use Moose;
extends 'Foo';
with 'Warning::NotInit';
sub m1 { say "in Bar::m1" }
package main;
use feature 'say';
在子类中调用 not 重写方法时,before
方法被称为两次。
my $bar = Bar->new;
say "bar the wrong way:";
$bar->m1;
$bar->m2;
输出:
bar the wrong way:
==> Bar is not initialized
in Bar::m1
==> Bar is not initialized
==> Bar is not initialized
in Foo::m2
为什么要两次调用?
答案 0 :(得分:2)
是的,这不是方法修饰符的工作方式。 before
修饰符成为方法本身的的一部分。当你重写子类中的方法时,你将覆盖整个超类的行为 - 也就是说,你也覆盖了方法修饰符。
您可以通过将方法修饰符分解为可应用于每个类的角色来解决此问题,如下所示:
package Warning::NotInit;
use feature 'say';
use Moose::Role;
has '_init' => (
is => 'rw',
isa => 'Bool',
default => 0
);
before method => sub {
my $self = shift;
my $class = ref($self);
warn "==> $class is not initialized\n" unless $self->_init;
};
package Foo;
use feature 'say';
use Moose;
with 'Warning::NotInit';
sub init {
shift->_init(1);
}
sub method {
say "in Foo::method";
}
package Bar;
use feature 'say';
use Moose;
extends 'Foo';
with 'Warning::NotInit';
sub method {
say "in Bar::method";
}
package main;
use feature 'say';
my $foo = Foo->new;
say "foo the wrong way:";
$foo->method;
say "foo the right way:";
$foo->init;
$foo->method;
my $bar = Bar->new;
say "bar the wrong way:";
$bar->method;