1Moose::Cookbook::Roles:U:sCeormpCaornatbrMlioebo_usCteoe:dd:eCRPoeeourkslbeo(Do3ok)c:u:mReonlteast:i:oCnomparable_CodeReuse(3)
2
3
4
6 Moose::Cookbook::Roles::Comparable_CodeReuse - Using roles for code
7 reuse
8
10 version 2.2011
11
13 package Eq;
14 use Moose::Role;
15
16 requires 'equal_to';
17
18 sub not_equal_to {
19 my ( $self, $other ) = @_;
20 not $self->equal_to($other);
21 }
22
23 package Comparable;
24 use Moose::Role;
25
26 with 'Eq';
27
28 requires 'compare';
29
30 sub equal_to {
31 my ( $self, $other ) = @_;
32 $self->compare($other) == 0;
33 }
34
35 sub greater_than {
36 my ( $self, $other ) = @_;
37 $self->compare($other) == 1;
38 }
39
40 sub less_than {
41 my ( $self, $other ) = @_;
42 $self->compare($other) == -1;
43 }
44
45 sub greater_than_or_equal_to {
46 my ( $self, $other ) = @_;
47 $self->greater_than($other) || $self->equal_to($other);
48 }
49
50 sub less_than_or_equal_to {
51 my ( $self, $other ) = @_;
52 $self->less_than($other) || $self->equal_to($other);
53 }
54
55 package Printable;
56 use Moose::Role;
57
58 requires 'to_string';
59
60 package US::Currency;
61 use Moose;
62
63 with 'Comparable', 'Printable';
64
65 has 'amount' => ( is => 'rw', isa => 'Num', default => 0 );
66
67 sub compare {
68 my ( $self, $other ) = @_;
69 $self->amount <=> $other->amount;
70 }
71
72 sub to_string {
73 my $self = shift;
74 sprintf '$%0.2f USD' => $self->amount;
75 }
76
78 Roles have two primary purposes: as interfaces, and as a means of code
79 reuse. This recipe demonstrates the latter, with roles that define
80 comparison and display code for objects.
81
82 Let's start with "Eq". First, note that we've replaced "use Moose" with
83 "use Moose::Role". We also have a new sugar function, "requires":
84
85 requires 'equal_to';
86
87 This says that any class which consumes this role must provide an
88 "equal_to" method. It can provide this method directly, or by consuming
89 some other role.
90
91 The "Eq" role defines its "not_equal_to" method in terms of the
92 required "equal_to" method. This lets us minimize the methods that
93 consuming classes must provide.
94
95 The next role, "Comparable", builds on the "Eq" role. We include "Eq"
96 in "Comparable" using "with", another new sugar function:
97
98 with 'Eq';
99
100 The "with" function takes a list of roles to consume. In our example,
101 the "Comparable" role provides the "equal_to" method required by "Eq".
102 However, it could opt not to, in which case a class that consumed
103 "Comparable" would have to provide its own "equal_to". In other words,
104 a role can consume another role without providing any required methods.
105
106 The "Comparable" role requires a method, "compare":
107
108 requires 'compare';
109
110 The "Comparable" role also provides a number of other methods, all of
111 which ultimately rely on "compare".
112
113 sub equal_to {
114 my ( $self, $other ) = @_;
115 $self->compare($other) == 0;
116 }
117
118 sub greater_than {
119 my ( $self, $other ) = @_;
120 $self->compare($other) == 1;
121 }
122
123 sub less_than {
124 my ( $self, $other ) = @_;
125 $self->compare($other) == -1;
126 }
127
128 sub greater_than_or_equal_to {
129 my ( $self, $other ) = @_;
130 $self->greater_than($other) || $self->equal_to($other);
131 }
132
133 sub less_than_or_equal_to {
134 my ( $self, $other ) = @_;
135 $self->less_than($other) || $self->equal_to($other);
136 }
137
138 Finally, we define the "Printable" role. This role exists solely to
139 provide an interface. It has no methods, just a list of required
140 methods. In this case, it just requires a "to_string" method.
141
142 An interface role is useful because it defines both a method and a
143 name. We know that any class which does this role has a "to_string"
144 method, but we can also assume that this method has the semantics we
145 want. Presumably, in real code we would define those semantics in the
146 documentation for the "Printable" role. (1)
147
148 Finally, we have the "US::Currency" class which consumes both the
149 "Comparable" and "Printable" roles.
150
151 with 'Comparable', 'Printable';
152
153 It also defines a regular Moose attribute, "amount":
154
155 has 'amount' => ( is => 'rw', isa => 'Num', default => 0 );
156
157 Finally we see the implementation of the methods required by our roles.
158 We have a "compare" method:
159
160 sub compare {
161 my ( $self, $other ) = @_;
162 $self->amount <=> $other->amount;
163 }
164
165 By consuming the "Comparable" role and defining this method, we gain
166 the following methods for free: "equal_to", "greater_than",
167 "less_than", "greater_than_or_equal_to" and "less_than_or_equal_to".
168
169 Then we have our "to_string" method:
170
171 sub to_string {
172 my $self = shift;
173 sprintf '$%0.2f USD' => $self->amount;
174 }
175
177 Roles can be very powerful. They are a great way of encapsulating
178 reusable behavior, as well as communicating (semantic and interface)
179 information about the methods our classes provide.
180
182 (1) Consider two classes, "Runner" and "Process", both of which define
183 a "run" method. If we just require that an object implements a
184 "run" method, we still aren't saying anything about what that
185 method actually does. If we require an object that implements the
186 "Executable" role, we're saying something about semantics.
187
189 · Stevan Little <stevan.little@iinteractive.com>
190
191 · Dave Rolsky <autarch@urth.org>
192
193 · Jesse Luehrs <doy@tozt.net>
194
195 · Shawn M Moore <code@sartak.org>
196
197 · יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
198
199 · Karen Etheridge <ether@cpan.org>
200
201 · Florian Ragwitz <rafl@debian.org>
202
203 · Hans Dieter Pearcey <hdp@weftsoar.net>
204
205 · Chris Prather <chris@prather.org>
206
207 · Matt S Trout <mst@shadowcat.co.uk>
208
210 This software is copyright (c) 2006 by Infinity Interactive, Inc.
211
212 This is free software; you can redistribute it and/or modify it under
213 the same terms as the Perl 5 programming language system itself.
214
215
216
217perl v5.28.1 Moo2s0e1:8:-C0o5o-k1b6ook::Roles::Comparable_CodeReuse(3)