This commit is contained in:
557
CPAN/Moose/Manual/Roles.pod
Normal file
557
CPAN/Moose/Manual/Roles.pod
Normal file
@@ -0,0 +1,557 @@
|
||||
# PODNAME: Moose::Manual::Roles
|
||||
# ABSTRACT: Roles, an alternative to deep hierarchies and base classes
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Moose::Manual::Roles - Roles, an alternative to deep hierarchies and base classes
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 2.2207
|
||||
|
||||
=head1 WHAT IS A ROLE?
|
||||
|
||||
A role encapsulates some piece of behavior or state that can be shared between
|
||||
classes. It is something that classes I<do>. It is important to understand that
|
||||
I<roles are not classes>. You cannot inherit from a role, and a role cannot be
|
||||
instantiated. We sometimes say that roles are I<consumed>, either by classes
|
||||
or other roles.
|
||||
|
||||
Instead, a role is I<composed> into a class. In practical terms, this
|
||||
means that all of the methods, method modifiers, and attributes defined in a role are
|
||||
added directly to (we sometimes say "flattened into") the class that
|
||||
consumes the role. These attributes and methods then appear as if they
|
||||
were defined in the class itself. A subclass of the consuming class
|
||||
will inherit all of these methods and attributes.
|
||||
|
||||
Moose roles are similar to mixins or interfaces in other languages and are
|
||||
based on the L<original concept of Traits|http://scg.unibe.ch/research/traits/>
|
||||
for the Smalltalk-80 dialect Squeak.
|
||||
|
||||
Besides defining their own methods and attributes, roles can also
|
||||
require that the consuming class define certain methods of its
|
||||
own. You could have a role that consisted only of a list of required
|
||||
methods, in which case the role would be very much like a Java
|
||||
interface.
|
||||
|
||||
Note that attribute accessors also count as methods for the
|
||||
purposes of satisfying the requirements of a role.
|
||||
|
||||
=head1 A SIMPLE ROLE
|
||||
|
||||
Creating a role looks a lot like creating a Moose class:
|
||||
|
||||
package Breakable;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
has 'is_broken' => (
|
||||
is => 'rw',
|
||||
isa => 'Bool',
|
||||
);
|
||||
|
||||
sub break {
|
||||
my $self = shift;
|
||||
|
||||
print "I broke\n";
|
||||
|
||||
$self->is_broken(1);
|
||||
}
|
||||
|
||||
Except for our use of L<Moose::Role>, this looks just like a class
|
||||
definition with Moose. However, this is not a class, and it cannot be
|
||||
instantiated.
|
||||
|
||||
Instead, its attributes and methods will be composed into classes
|
||||
which use the role:
|
||||
|
||||
package Car;
|
||||
|
||||
use Moose;
|
||||
|
||||
with 'Breakable';
|
||||
|
||||
has 'engine' => (
|
||||
is => 'ro',
|
||||
isa => 'Engine',
|
||||
);
|
||||
|
||||
The C<with> function composes roles into a class. Once that is done,
|
||||
the C<Car> class has an C<is_broken> attribute and a C<break>
|
||||
method. The C<Car> class also C<does('Breakable')>:
|
||||
|
||||
my $car = Car->new( engine => Engine->new );
|
||||
|
||||
print $car->is_broken ? 'Busted' : 'Still working';
|
||||
$car->break;
|
||||
print $car->is_broken ? 'Busted' : 'Still working';
|
||||
|
||||
$car->does('Breakable'); # true
|
||||
|
||||
This prints:
|
||||
|
||||
Still working
|
||||
I broke
|
||||
Busted
|
||||
|
||||
We could use this same role in a C<Bone> class:
|
||||
|
||||
package Bone;
|
||||
|
||||
use Moose;
|
||||
|
||||
with 'Breakable';
|
||||
|
||||
has 'marrow' => (
|
||||
is => 'ro',
|
||||
isa => 'Marrow',
|
||||
);
|
||||
|
||||
See also L<Moose::Cookbook::Roles::Comparable_CodeReuse> for an example.
|
||||
|
||||
It's possible to compose existing roles into new roles. For example, we can
|
||||
have a C<HandleWithCare> class which applies both the C<Breakable> and
|
||||
C<Package> roles to any class which consumes it:
|
||||
|
||||
package HandleWithCare;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
with 'Breakable', 'Package';
|
||||
|
||||
=head1 REQUIRED METHODS
|
||||
|
||||
As mentioned previously, a role can require that consuming classes
|
||||
provide one or more methods. Using our C<Breakable> example, let's
|
||||
make it require that consuming classes implement their own C<break>
|
||||
methods:
|
||||
|
||||
package Breakable;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
requires 'break';
|
||||
|
||||
has 'is_broken' => (
|
||||
is => 'rw',
|
||||
isa => 'Bool',
|
||||
);
|
||||
|
||||
after 'break' => sub {
|
||||
my $self = shift;
|
||||
|
||||
$self->is_broken(1);
|
||||
};
|
||||
|
||||
If we try to consume this role in a class that does not have a
|
||||
C<break> method, we will get an exception.
|
||||
|
||||
You can see that we added a method modifier on C<break>. We want
|
||||
classes that consume this role to implement their own logic for
|
||||
breaking, but we make sure that the C<is_broken> attribute is always
|
||||
set to true when C<break> is called.
|
||||
|
||||
package Car
|
||||
|
||||
use Moose;
|
||||
|
||||
with 'Breakable';
|
||||
|
||||
has 'engine' => (
|
||||
is => 'ro',
|
||||
isa => 'Engine',
|
||||
);
|
||||
|
||||
sub break {
|
||||
my $self = shift;
|
||||
|
||||
if ( $self->is_moving ) {
|
||||
$self->stop;
|
||||
}
|
||||
}
|
||||
|
||||
=head2 Roles Versus Abstract Base Classes
|
||||
|
||||
If you are familiar with the concept of abstract base classes in other
|
||||
languages, you may be tempted to use roles in the same way.
|
||||
|
||||
You I<can> define an "interface-only" role, one that contains I<just>
|
||||
a list of required methods.
|
||||
|
||||
However, any class which consumes this role must implement all of the
|
||||
required methods, either directly or through inheritance from a
|
||||
parent. You cannot delay the method requirement check so that they can
|
||||
be implemented by future subclasses.
|
||||
|
||||
Because the role defines the required methods directly, adding a base
|
||||
class to the mix would not achieve anything. We recommend that you
|
||||
simply consume the interface role in each class which implements that
|
||||
interface.
|
||||
|
||||
=head1 CONSUMING ROLES
|
||||
|
||||
Roles are consumed using the C<with> function.
|
||||
|
||||
Most of the time, you should only use one C<with>, even if you are consuming
|
||||
multiple roles. If you consume roles using multiple C<with> statements Moose
|
||||
cannot detect method conflicts between those roles.
|
||||
|
||||
Roles can be consumed by classes or by other roles. When a class consumes a
|
||||
role which in turn consumes other roles, the class gets all of the roles
|
||||
applied at once.
|
||||
|
||||
=head2 Required Methods Provided by Attributes
|
||||
|
||||
As mentioned before, a role's required method may also be satisfied by an
|
||||
attribute accessor. However, the call to C<has> which defines an attribute
|
||||
happens at runtime. This means that you must define the attribute I<before>
|
||||
consuming the role, or else the role will not see the generated accessor.
|
||||
These attributes are L<Moose Attributes|Moose::Manual::Attributes>.
|
||||
|
||||
package Breakable;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
requires 'stress';
|
||||
|
||||
########
|
||||
|
||||
package Car;
|
||||
|
||||
use Moose;
|
||||
|
||||
has 'stress' => (
|
||||
is => 'ro',
|
||||
isa => 'Int',
|
||||
);
|
||||
|
||||
with 'Breakable';
|
||||
|
||||
In general, we recommend that you always consume roles I<after> declaring all
|
||||
your attributes.
|
||||
|
||||
It may also be the case that a class wants to consume two roles where one role
|
||||
has an attribute providing a required method for another. For example:
|
||||
|
||||
package Breakable;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
requires 'stress';
|
||||
|
||||
########
|
||||
|
||||
package Stressable;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
has 'stress' => (
|
||||
is => 'ro',
|
||||
isa => 'Int',
|
||||
);
|
||||
|
||||
########
|
||||
|
||||
package Car;
|
||||
|
||||
use Moose;
|
||||
|
||||
# XXX - this will not work
|
||||
with 'Breakable', 'Stressable';
|
||||
|
||||
However, this won't work. The problem is that the accessor methods created for
|
||||
the C<stress> attribute won't be present in the class when the required method
|
||||
checks are done.
|
||||
|
||||
There are two possible workarounds. The recommended one is to use "stub"
|
||||
subroutine(s) in the role providing the accessor(s):
|
||||
|
||||
package Stressable;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
sub stress;
|
||||
has 'stress' => (
|
||||
is => 'ro',
|
||||
isa => 'Int',
|
||||
);
|
||||
|
||||
The C<sub stress;> line is called a "forward" declaration in the Perl
|
||||
documentation. It creates what is called a "stub" subroutine, a declaration
|
||||
without a body. This is good enough to satisfy the required method checks done
|
||||
by Moose. The stub will not interfere with the creation of a real subroutine
|
||||
later.
|
||||
|
||||
The other alternative is to use two separate calls to C<with> in the consuming
|
||||
class:
|
||||
|
||||
package Car;
|
||||
|
||||
use Moose;
|
||||
|
||||
# Not recommended
|
||||
with 'Stressable';
|
||||
with 'Breakable';
|
||||
|
||||
Each C<with> is run as it is seen. The first call will consume just the
|
||||
C<Stressable> role, which will add the C<stress> attribute to the C<Car>
|
||||
package, which in turn will create an accessor method named C<stress>. Then
|
||||
when the C<Breakable> role is consumed, the method it requires already exists.
|
||||
|
||||
However, as mentioned earlier, multiple C<with> declarations are not
|
||||
recommended, because method conflicts between the roles cannot be seen. In the
|
||||
example above, if both C<Stressable> and C<Breakable> contained methods of the
|
||||
same name, what would happen is that the version in C<Stressable> would
|
||||
I<silently> override the one in C<Breakable>.
|
||||
|
||||
=head1 USING METHOD MODIFIERS
|
||||
|
||||
Method modifiers and roles are a very powerful combination. Often, a
|
||||
role will combine method modifiers and required methods. We already
|
||||
saw one example with our C<Breakable> example.
|
||||
|
||||
Method modifiers increase the complexity of roles, because they make
|
||||
the role application order relevant. If a class uses multiple roles,
|
||||
each of which modify the same method, those modifiers will be applied
|
||||
in the same order as the roles are used:
|
||||
|
||||
package MovieCar;
|
||||
|
||||
use Moose;
|
||||
|
||||
extends 'Car';
|
||||
|
||||
with 'Breakable', 'ExplodesOnBreakage';
|
||||
|
||||
Assuming that the new C<ExplodesOnBreakage> role I<also> has an
|
||||
C<after> modifier on C<break>, the C<after> modifiers will run one
|
||||
after the other. The modifier from C<Breakable> will run first, then
|
||||
the one from C<ExplodesOnBreakage>.
|
||||
|
||||
=head1 METHOD CONFLICTS
|
||||
|
||||
If a class composes multiple roles, and those roles have methods of
|
||||
the same name, we will have a conflict. In that case, the composing
|
||||
class is required to provide its I<own> method of the same name.
|
||||
|
||||
package Breakdancer;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
sub break {
|
||||
|
||||
}
|
||||
|
||||
If we compose both C<Breakable> and C<Breakdancer> in a class, we must
|
||||
provide our own C<break> method:
|
||||
|
||||
package FragileDancer;
|
||||
|
||||
use Moose;
|
||||
|
||||
with 'Breakable', 'Breakdancer';
|
||||
|
||||
sub break { ... }
|
||||
|
||||
A role can be a collection of other roles:
|
||||
|
||||
package Break::Bundle;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
with ('Breakable', 'Breakdancer');
|
||||
|
||||
When a role consumes another a role, the I<consuming> role's methods silently
|
||||
win in any conflict, and the consumed role's methods are simply ignored.
|
||||
|
||||
=head1 METHOD EXCLUSION AND ALIASING
|
||||
|
||||
If we want our C<FragileDancer> class to be able to call the methods
|
||||
from both its roles, we can alias the methods:
|
||||
|
||||
package FragileDancer;
|
||||
|
||||
use Moose;
|
||||
|
||||
with 'Breakable' => { -alias => { break => 'break_bone' } },
|
||||
'Breakdancer' => { -alias => { break => 'break_dance' } };
|
||||
|
||||
However, aliasing a method simply makes a I<copy> of the method with
|
||||
the new name. We also need to exclude the original name:
|
||||
|
||||
with 'Breakable' => {
|
||||
-alias => { break => 'break_bone' },
|
||||
-excludes => 'break',
|
||||
},
|
||||
'Breakdancer' => {
|
||||
-alias => { break => 'break_dance' },
|
||||
-excludes => 'break',
|
||||
};
|
||||
|
||||
The excludes parameter prevents the C<break> method from being composed
|
||||
into the C<FragileDancer> class, so we don't have a conflict. This
|
||||
means that C<FragileDancer> does not need to implement its own
|
||||
C<break> method.
|
||||
|
||||
This is useful, but it's worth noting that this breaks the contract
|
||||
implicit in consuming a role. Our C<FragileDancer> class does both the
|
||||
C<Breakable> and C<BreakDancer>, but does not provide a C<break>
|
||||
method. If some API expects an object that does one of those roles, it
|
||||
probably expects it to implement that method.
|
||||
|
||||
In some use cases we might alias and exclude methods from roles, but
|
||||
then provide a method of the same name in the class itself.
|
||||
|
||||
Also see L<Moose::Cookbook::Roles::Restartable_AdvancedComposition> for an example.
|
||||
|
||||
=head1 OVERLOADING
|
||||
|
||||
When a Moose role uses overloading, that overloading is composed into any
|
||||
classes that consume the role. This includes the setting of the C<fallback>
|
||||
value for that role's overloading. Just as with methods and attributes, when a
|
||||
role consumes another role, that other role's overloading settings are applied
|
||||
to the role.
|
||||
|
||||
Just as with methods, there can be conflicts with overloading implementations
|
||||
between multiple roles when they are all consumed by a class. If two roles
|
||||
both provide different overloading implementations for a given operator, that
|
||||
is a conflict. If two roles both implement overloading and have different
|
||||
C<fallback> values, that is also considered a conflict. These conflicts are
|
||||
detected when multiple roles are being composed into a class together.
|
||||
|
||||
When a role consumes another role, the consuming role's overloading fallback
|
||||
and operator implementations silently "win" the conflict.
|
||||
|
||||
=head1 ROLE EXCLUSION
|
||||
|
||||
A role can say that it cannot be combined with some other role. This
|
||||
should be used with great caution, since it limits the re-usability of
|
||||
the role.
|
||||
|
||||
package Breakable;
|
||||
|
||||
use Moose::Role;
|
||||
|
||||
excludes 'BreakDancer';
|
||||
|
||||
=head1 ADDING A ROLE TO AN OBJECT INSTANCE
|
||||
|
||||
You may want to add a role to an object instance, rather than to a class. For
|
||||
example, you may want to add debug tracing to one instance of an object while
|
||||
debugging a particular bug. Another use case might be to dynamically change
|
||||
objects based on a user's configuration, as a plugin system.
|
||||
|
||||
The best way to do this is to use the C<apply_all_roles()> function from
|
||||
L<Moose::Util>:
|
||||
|
||||
use Moose::Util qw( apply_all_roles );
|
||||
|
||||
my $car = Car->new;
|
||||
apply_all_roles( $car, 'Breakable' );
|
||||
|
||||
This function can apply more than one role at a time, and will do so using the
|
||||
normal Moose role combination system. We recommend using this function to
|
||||
apply roles to an object. This is what Moose uses internally when you call
|
||||
C<with>.
|
||||
|
||||
=head2 Handling required attributes for roles.
|
||||
|
||||
Application of some roles will require additional parameters being specified to
|
||||
satisfy them, for example:
|
||||
|
||||
{
|
||||
package Car;
|
||||
use Moose;
|
||||
}
|
||||
{
|
||||
package Breakable;
|
||||
use Moose::Role;
|
||||
|
||||
has 'breakable_parts' => ( is => 'ro', required => 1 );
|
||||
}
|
||||
|
||||
my $car = Car->new;
|
||||
|
||||
# next line dies with: Attribute (breakable_parts) is required
|
||||
apply_all_roles( $car, 'Breakable' );
|
||||
|
||||
This will require passing the additional parameters at application time as
|
||||
follows:
|
||||
|
||||
apply_all_roles( $car, 'Breakable' => {
|
||||
rebless_params => {
|
||||
# Parameters to 'Breakable'
|
||||
breakable_parts => [qw( tires wheels windscreen )],
|
||||
}
|
||||
});
|
||||
|
||||
Obviously, this interface is better simplified as a method on C<Car>:
|
||||
|
||||
sub make_breakable {
|
||||
my ( $self, %params ) = @_;
|
||||
apply_all_roles($self, 'Breakable', { rebless_params => \%params });
|
||||
}
|
||||
|
||||
my $car = Car->new();
|
||||
$car->make_breakable( breakable_parts => [qw( tires wheels windscreen )] );
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Stevan Little <stevan@cpan.org>
|
||||
|
||||
=item *
|
||||
|
||||
Dave Rolsky <autarch@urth.org>
|
||||
|
||||
=item *
|
||||
|
||||
Jesse Luehrs <doy@cpan.org>
|
||||
|
||||
=item *
|
||||
|
||||
Shawn M Moore <sartak@cpan.org>
|
||||
|
||||
=item *
|
||||
|
||||
יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
|
||||
|
||||
=item *
|
||||
|
||||
Karen Etheridge <ether@cpan.org>
|
||||
|
||||
=item *
|
||||
|
||||
Florian Ragwitz <rafl@debian.org>
|
||||
|
||||
=item *
|
||||
|
||||
Hans Dieter Pearcey <hdp@cpan.org>
|
||||
|
||||
=item *
|
||||
|
||||
Chris Prather <chris@prather.org>
|
||||
|
||||
=item *
|
||||
|
||||
Matt S Trout <mstrout@cpan.org>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
This software is copyright (c) 2006 by Infinity Interactive, Inc.
|
||||
|
||||
This is free software; you can redistribute it and/or modify it under
|
||||
the same terms as the Perl 5 programming language system itself.
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user