修改或提供方法的角色

Role that modifies or provides a method

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

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

具体例子:

package UsualFavorites;
use Moose::Role;

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

如果消费 class 没有定义 favorite_things 方法,我希望它以 favorite_things 方法结束,只是 returns (doorbells, sleighbells) .

可以使用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

请注意,如果您的角色被另一个参数化角色使用,或者如果您的角色应用于对象实例,则您必须执行额外的操作。 Ether describes both of these cases in How can I access the meta class of the module my Moose role is being applied to? 并在评论中指出:

I no longer consider the above a "best practice", and indeed have refactored out all of this (ab)use of MXRP. IMHO if you need to access $meta from within a role, you have something stinky in your design.

你有什么理由不能简单地使 favorite_things 成为必需的吗?

采用 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);

所以我都有 我在角色中有默认实现,它似乎有效。

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

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/;
}