Moose::Cookbook::Roles::Recipe1(3pm)
NAME
Moose::Cookbook::Roles::Recipe1 - The Moose::Role example
SYNOPSIS
package Eq;
use Moose::Role;
requires 'equal_to';
sub not_equal_to {
my ( $self, $other ) = @_;
not $self->equal_to($other);
}
package Comparable;
use Moose::Role;
with 'Eq';
requires 'compare';
sub equal_to {
my ( $self, $other ) = @_;
$self->compare($other) == 0;
}
sub greater_than {
my ( $self, $other ) = @_;
$self->compare($other) == 1;
}
sub less_than {
my ( $self, $other ) = @_;
$self->compare($other) == -1;
}
sub greater_than_or_equal_to {
my ( $self, $other ) = @_;
$self->greater_than($other) || $self->equal_to($other);
}
sub less_than_or_equal_to {
my ( $self, $other ) = @_;
$self->less_than($other) || $self->equal_to($other);
}
package Printable;
use Moose::Role;
requires 'to_string';
package US::Currency;
use Moose;
with 'Comparable', 'Printable';
has 'amount' => ( is => 'rw', isa => 'Num', default => 0 );
sub compare {
my ( $self, $other ) = @_;
$self->amount <=> $other->amount;
}
sub to_string {
my $self = shift;
sprintf '$%0.2f USD' => $self->amount;
}
DESCRIPTION
Roles have two primary purposes: as interfaces, and as a means of code
reuse. This recipe demonstrates the latter, with roles that define
comparison and display code for objects.
- Let's start with "Eq". First, note that we've replaced "use Moose" with
"use Moose::Role". We also have a new sugar function, "requires":
- requires 'equal_to';
- This says that any class which consumes this role must provide an
"equal_to" method. It can provide this method directly, or by consuming some other role. - The "Eq" role defines its "not_equal_to" method in terms of the
required "equal_to" method. This lets us minimize the methods that
consuming classes must provide. - The next role, "Comparable", builds on the "Eq" role. We include "Eq"
in "Comparable" using "with", another new sugar function:
with 'Eq';- The "with" function takes a list of roles to consume. In our example,
the "Comparable" role provides the "equal_to" method required by "Eq". However, it could opt not to, in which case a class that consumed
"Comparable" would have to provide its own "equal_to". In other words, a role can consume another role without providing any required methods. - The "Comparable" role requires a method, "compare":
requires 'compare';- The "Comparable" role also provides a number of other methods, all of
which ultimately rely on "compare".
sub equal_to {my ( $self, $other ) = @_;
$self->compare($other) == 0;- }
- sub greater_than {
my ( $self, $other ) = @_;
$self->compare($other) == 1; - }
- sub less_than {
my ( $self, $other ) = @_;
$self->compare($other) == -1; - }
- sub greater_than_or_equal_to {
my ( $self, $other ) = @_;
$self->greater_than($other) || $self->equal_to($other); - }
- sub less_than_or_equal_to {
my ( $self, $other ) = @_;
$self->less_than($other) || $self->equal_to($other); - }
- Finally, we define the "Printable" role. This role exists solely to
provide an interface. It has no methods, just a list of required
methods. In this case, it just requires a "to_string" method. - An interface role is useful because it defines both a method and a
name. We know that any class which does this role has a "to_string" method, but we can also assume that this method has the semantics we
want. Presumably, in real code we would define those semantics in the
documentation for the "Printable" role. (1) - Finally, we have the "US::Currency" class which consumes both the
"Comparable" and "Printable" roles.
with 'Comparable', 'Printable';- It also defines a regular Moose attribute, "amount":
has 'amount' => ( is => 'rw', isa => 'Num', default => 0 );- Finally we see the implementation of the methods required by our roles. We have a "compare" method:
sub compare {my ( $self, $other ) = @_;
$self->amount <=> $other->amount;- }
- By consuming the "Comparable" role and defining this method, we gain
the following methods for free: "equal_to", "greater_than",
"less_than", "greater_than_or_equal_to" and "less_than_or_equal_to". - Then we have our "to_string" method:
sub to_string {my $self = shift;
sprintf '$%0.2f USD' => $self->amount;- }
CONCLUSION
Roles can be very powerful. They are a great way of encapsulating
reusable behavior, as well as communicating (semantic and interface)
information about the methods our classes provide.
FOOTNOTES
- (1) Consider two classes, "Runner" and "Process", both of which define
- a "run" method. If we just require that an object implements a
"run" method, we still aren't saying anything about what that
method actually does. If we require an object that implements the "Executable" role, we're saying something about semantics.
AUTHORS
Stevan Little <stevan@iinteractive.com>
Dave Rolsky <autarch@urth.org>
COPYRIGHT AND LICENSE
Copyright 2006-2010 by Infinity Interactive, Inc.
<http://www.iinteractive.com>
- This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.