Moose::Role 覆盖方法的怪异之处

Moose::Role weirdness with overridden methods

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

documentation describes override为:

An override method is a way of explicitly saying "I am overriding this method from my superclass". You can call super within this method, and it will work as expected. The same thing can be accomplished with a normal method call and the SUPER:: pseudo-package; it is really your choice.

你所做的与这个定义相矛盾。有了角色,f 就安装在你的包中。您正试图在同一个包中定义另一个 f

你的角色被称为 Base 这一事实向我表明你对继承与组合之间的区别有些困惑。

purpose of around是在当前class中包装一个方法,不管是在同一个包中实现的还是继承或组合的:

Method modifiers can be used to add behavior to methods without modifying the definition of those methods.

只要简单阅读这两个片段,我就会清楚其中的区别。

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

将此视为航空旅行。将方法修饰符视为 classes:首先,商业、经济等。因此,首先 class、get_dinner_menu 可能包裹着开胃菜、糖果、甜点等。

另一方面,override就像改变航班一样。就好像你在说 "I want to fly on both TK 1 and 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