Moose ::使用重写方法的角色怪异

时间:2016-06-30 16:46:55

标签: perl oop override moose perl5

Base.pm

package Base;
use Moose::Role;

sub f {
  my ($self) = @_;
  print "In role.\n";
}

1;

X.pm

package X;
use Moose;

with 'Base';

around 'f' => sub {
  my ($next, $self) = @_;
  print "Nevermind, we are in class X.\n";
};

__PACKAGE__->meta->make_immutable;
1;

Y.pm

package Y;
use Moose;

with 'Base';

override 'f' => sub {
  my ($self) = @_;
  print "Nevermind, we are in class Y.\n";
};

__PACKAGE__->meta->make_immutable;
1;

然后X确实有效,Y没有。这是一个奇怪的设计,因为override只是around的一个特例,并且特殊情况也应该起作用。

任何人都可以解释为什么这个设计决定以及它为何如此奇怪?

$ perl X.pm 
$ perl Y.pm 
Cannot add an override method if a local method is already present at /usr/lib/i386-linux-gnu/perl5/5.22/Moose/Exporter.pm line 419
    Moose::override('f', 'CODE(0x9c002f8)') called at Y.pm line 9

1 个答案:

答案 0 :(得分:3)

documentation describes override为:

  

覆盖方法是一种明确说“我从我的超类中覆盖此方法”的方法。您可以在此方法中调用super,它将按预期工作。使用普通方法调用和SUPER::伪包可以完成同样的事情;这真的是你的选择。

你在做什么与这个定义相矛盾。使用角色,您的包中会安装f。您正尝试在同一个包中定义另一个f

你的角色被称为Base这一事实向我表明,当谈到继承与构成之间的区别时,你会有一些困惑。

purpose of around是在当前类中包装一个方法,无论它是在同一个包中实现还是继承或组合:

  

方法修饰符可用于向方法添加行为,而无需修改这些方法的定义。

只需简单地阅读这两个片段就可以让我明白这一点。

当您应用定义f的角色时,它本身会覆盖任何继承的方法f。如果您再说override 'f',则表示您打算再次覆盖f。好吧,一个类中只能有一个方法f。哪一个应该算?通过应用角色或刚刚定义的角色获得的角色?对于所有意图和目的,组成角色所获得的方法就像您在类中手动定义的方法一样。没有理由a priori一个比另一个更重要。

将此视为航空旅行。将方法修饰符视为类:首先,商业,经济等等。因此,在第一类中,get_dinner_menu可能包含开胃菜,糖果,甜点等。

另一方面,override就像改变航班一样。这就好像你说“我想要在TK 1和UIA 213上飞行”。毫无意义。

通过显示around的简单实现并在不使用override的情况下覆盖方法,以下脚本可能会使事情变得更清晰。

#!/usr/bin/env perl

use strict;
use warnings;

{
    package MyRole;

    use Moose::Role;

    sub f {
        print "in role\n";
    }
}

{
    package X;
    use Moose;

    with 'MyRole';

    around 'f' => sub {
        my ($orig, $self) = @_;
        print "In wrapper\n";
        return $self->$orig( @_ );
    };

    {
        my $f = \&f;
        {
            no warnings 'redefine';
            *f = sub {
                my ($self) = @_;
                print "In wrapper wrapper\n";
                return $self->$f( @_ );
            }
        }
    }
}

{
    package Y;

    use Moose;
    with 'MyRole';

    sub f {
        print "In overridden method\n";
    }
}

print '=-=' x 22, "\n";

my $x = X->new;
$x->f;

print '=-=' x 22, "\n";

my $y = Y->new;
$y->f;

输出:

=-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-= 
In wrapper wrapper                                                 
In wrapper                                                         
in role                                                            
=-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-= 
In overridden method