修改或提供方法的角色
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/;
}
如果我想要一个角色来修改一个方法,如果消费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/;
}