【问题标题】:Moo(se), before and inheritanceMoo(se),之前和继承
【发布时间】:2014-02-03 09:44:22
【问题描述】:

我有一个 Moo(se)[0] 类,其中包含许多在顶部具有完全相同类型的“保护语句”的方法。与其多次编写相同的代码,我想我可以将语句放在“之前”方法修饰符中,并且效果很好。除非这个类是子类,否则永远不会调用“之前的守卫”。

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

我认为这种行为是设计使然,但是有什么(好的)方法可以确保所有子类也继承“之前”方法修饰符/保护语句?或者是否有不同的方法来实现这一点(我怀疑这是一个相当常见的结构)。请注意,在真正的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

为什么要调用两次?

【问题讨论】:

  • 另外,我要说的是,您的总体目标似乎是确保永远不会在不完整/不一致状态的对象上调用特定方法。更好的解决方案是确保您的对象永远不会处于该状态。编写一个BUILD 子程序,确保您的对象在构造后立即进入完整/一致状态,并确保您的方法永远不会再次将其恢复为不完整/不一致状态。
  • 在以前的代码迭代中,我确实将所有设置代码放在BUILD 中,但这导致子类化变得困难/混乱。特定的类是 Net::Telnet 的包装器,并针对特定的目标平台进行了调整。部分子类需要将参数调整为Net::Telnet->new(在父类BUILD中),但由于子类BUILD是在父类之后调用的,所以为时已晚。

标签: perl moose


【解决方案1】:

是的,方法修饰符不是这样工作的。 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;

【讨论】:

  • 嗯,使用Role 绝对是一个聪明的主意。走这条路的唯一坏事是角色和消费者类之间的紧密耦合,因为角色必须确切地知道应该修改哪些方法。但对于私人课程,我认为这不是一个大问题。
  • 如果您使用参数化角色,则可以将要修改的方法列表作为with 语句的一部分传递给它。 MooseX::Deprecated 做了类似的事情。
猜你喜欢
  • 2020-01-06
  • 2017-12-09
  • 2012-01-24
  • 1970-01-01
  • 2010-10-26
  • 1970-01-01
相关资源
最近更新 更多