Moose::Role 带有重写方法的怪异

Posted

技术标签:

【中文标题】Moose::Role 带有重写方法的怪异【英文标题】:Moose::Role weirdness with overridden methods 【发布时间】:2016-06-30 16:46:55 【问题描述】:

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】:

documentation describes override 为:

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

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

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

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

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

只要简单地阅读这两个 sn-ps,我就可以清楚地区分它们。

当您应用定义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;

输出:

=-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==- ==-==-==-==-==-==-=
在包装器中
在包装中
在角色中
=-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==- ==-==-==-==-==-=
在被覆盖的方法中

【讨论】:

但是为什么在地球上,around 在这种情况下确实有效?不能对around 说同样的话来证明它不应该工作吗? 知道了。但是允许 both subaround 在一个类中使用相同的方法对我来说似乎是一个设计错误 在同一个包中禁止 sub faround 'f' 没有任何好处,因为组成一个提供@987654347 的角色@ 将 f 安装为包中的子项。因此,该禁令使得将around 与组合方法一起使用变得不可能或困难。我没有想太多。

以上是关于Moose::Role 带有重写方法的怪异的主要内容,如果未能解决你的问题,请参考以下文章

文档的滚动条标准怪异模式视口尺寸

重载的包私有方法导致编译失败 - 这是 JLS 怪异还是 javac 错误?

MybatisPlus重写SQL分析方法

位置服务行为怪异

浏览器的标准模式与怪异模式的设置与区分方法

1477.怪异的洗牌