【问题标题】:Role that modifies or provides a method修改或提供方法的角色
【发布时间】:2015-04-25 05:21:42
【问题描述】:

如果我想要一个角色修改消费类没有的方法,或者提供消费类没有的默认方法怎么办? p>

在一种情况下,使用方法修饰符是可行的。在另一种情况下,只需定义一个普通方法即可。有两种情况都适用的方法吗?

具体例子:

package UsualFavorites;
use Moose::Role;

around favorite_things {
    my ($self, $orig) = @_;
    $self->$orig(), qw/doorbells sleighbells/;
}

如果消费类没有定义favorite_things 方法,我希望它以只返回(doorbells, sleighbells)favorite_things 方法结束。

【问题讨论】:

    标签: perl moose


    【解决方案1】:

    只需在角色中定义方法即可。如果该类有一个同名的方法,那么角色中的方法将被忽略。

    package UsualFavorites;
    use Moose::Role;
    
    sub favorite_things {
        return ();
    }
    around favorite_things => sub {
        my ($orig, $self) = @_;
        return ($self->$orig(), qw/doorbells sleighbells/);
    };
    
    package Consumer;
    use Moose;
    with 'UsualFavorites';
    
    sub favorite_things {
        return qw/shipbells/;
    }
    

    【讨论】:

    【解决方案2】:

    可以使用MooseX::Role::Parameterized

    Favorites.pm

    package Favorites;
    
    use MooseX::Role::Parameterized;
    
    parameter method_name => (
        isa     => 'Str',
        default => 'favorite_things'
    );
    
    role {
        my $p = shift;
        my %args = @_;
        my $consumer = $args{consumer};
    
        my $method_name = $p->method_name;
        my @default_values = qw/doorbells sleighbells/;
    
        if ( $consumer->find_method_by_name($method_name) ) {
            around $method_name => sub {
                my $orig = shift;
                my $self = shift;
    
                $self->$orig(@_), @default_values;
            };
        }
        else {
            method $method_name => sub {
                my $self = shift;
    
                return @default_values;
            };
        }
    };
    
    no Moose::Role;
    
    1;
    

    Santa.pm(圣诞老人喜欢门铃,对吧?):

    package Santa;
    
    use Moose;
    use namespace::autoclean;
    
    with 'Favorites';
    
    __PACKAGE__->meta->make_immutable;
    
    1;
    

    ACDC.pm

    package ACDC;
    
    use Moose;
    use namespace::autoclean;
    
    with 'Favorites';
    
    sub favorite_things {
        my $self = shift;
    
        return 'Hells Bells';
    }
    
    __PACKAGE__->meta->make_immutable;
    
    1;
    

    favorites_test

    use strict;
    use warnings;
    use 5.010;
    
    use ACDC;
    use Santa;
    
    my $kris_kringle = Santa->new;
    say 'Santa likes ', join(', ', $kris_kringle->favorite_things);
    
    my $acdc = ACDC->new;
    say 'AC/DC likes ', join(', ', $acdc->favorite_things);
    

    输出:

    Santa likes doorbells, sleighbells
    AC/DC likes Hells Bells, doorbells, sleighbells
    

    请注意,如果您的角色被另一个参数化角色使用,或者如果您的角色应用于对象实例,则您必须执行额外的操作。 EtherHow can I access the meta class of the module my Moose role is being applied to? 中描述了这两种情况,并在评论中指出:

    我不再认为上述是“最佳实践”,并且确实已经重构了所有这些(ab)对 MXRP 的使用。恕我直言,如果您需要在某个角色中访问 $meta,那么您的设计中就有一些臭名昭著的东西。

    您有什么理由不能简单地将favorite_things 设为必需?

    【讨论】:

      【解决方案3】:

      采用 ThisSuitIsBlackNot 的解决方案并稍微简化一下,我有:

      package UsualFavorites;
      use Moose::Role;
      use strict;
      use warnings;
      
      around favorite_things => sub {
          my ($orig, $self) = @_;
          $self->$orig(), qw/doorbells sleighbells/;
      };
      
      sub favorite_things { () }
      
      package Santa;
      
      use Moose;
      use strict;
      use warnings;
      with 'UsualFavorites';
      
      package ACDC;
      
      use Moose;
      use strict;
      use warnings;
      with 'UsualFavorites';
      
      sub favorite_things {
          my $self = shift;
          return 'Hells Bells';
      }
      
      package main;
      
      use strict;
      use warnings;
      use 5.010;
      
      my $kris_kringle = Santa->new;
      say 'Santa likes ', join(', ', $kris_kringle->favorite_things);
      
      my $acdc = ACDC->new;
      say 'AC/DC likes ', join(', ', $acdc->favorite_things);
      

      所以我都有周围的我在角色中有默认实现,它似乎工作。

      【讨论】:

      • 很好,比我的方法干净得多。这也是Denis Ibaev suggested。至于“似乎有效”,丹尼斯说“如果类有一个同名的方法,那么角色中的方法将被忽略”,但我在任何地方的文档中都找不到。你知道这种行为是否被记录在案?
      • 这只是简单的、常规的旧继承。由于 Role 是父类,因此子类只是覆盖它。也可以拨打$self->SUPER::favorite_things,虽然这里没有必要。
      • 我认为这不对...角色不是父类。在消费类内部调用say for __PACKAGE__->meta->linearized_isa;可以看到继承关系;不返回角色名称。 $self->SUPER::favorite_things 起作用的原因是因为它解析为当前包中的方法(恰好名为favorite_things),而不是角色中的方法。在角色中创建foo方法,在消费类中调用$self->SUPER::foo;你会得到Can't locate object method "foo" via package "Consumer"
      • Denis Ibaev pointed out 如何将角色中的方法应用于类;实际上,如果方法已在消费类中定义,则会跳过它们:next if $class_method && $class_method->body != $method->body;
      猜你喜欢
      • 2021-01-04
      • 1970-01-01
      • 2021-07-10
      • 2017-11-20
      • 2011-02-17
      • 1970-01-01
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多