Fix some typos
[gitmo/Moose.git] / lib / Moose / Cookbook / Basics / Recipe2.pod
CommitLineData
471c4f09 1
2=pod
3
4=head1 NAME
5
021b8139 6Moose::Cookbook::Basics::Recipe2 - A simple B<BankAccount> example
471c4f09 7
8=head1 SYNOPSIS
9
10 package BankAccount;
471c4f09 11 use Moose;
12
13 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
14
15 sub deposit {
16 my ($self, $amount) = @_;
17 $self->balance($self->balance + $amount);
18 }
19
20 sub withdraw {
21 my ($self, $amount) = @_;
22 my $current_balance = $self->balance();
23 ($current_balance >= $amount)
24 || confess "Account overdrawn";
25 $self->balance($current_balance - $amount);
26 }
27
28 package CheckingAccount;
471c4f09 29 use Moose;
30
31 extends 'BankAccount';
32
33 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
34
35 before 'withdraw' => sub {
cdcae970 36 my ($self, $amount) = @_;
37 my $overdraft_amount = $amount - $self->balance();
e9bb8a31 38 if ($self->overdraft_account && $overdraft_amount > 0) {
cdcae970 39 $self->overdraft_account->withdraw($overdraft_amount);
40 $self->deposit($overdraft_amount);
41 }
471c4f09 42 };
43
44=head1 DESCRIPTION
45
9cf569f3 46The first recipe demonstrated how to build very basic Moose classes,
47focusing on creating and manipulating attributes. The objects in that
b0825978 48recipe were very data-oriented, and did not have much in the way of
9cf569f3 49behavior (i.e. methods). In this recipe, we expand upon the concepts
50from the first recipe to include some real behavior. In particular, we
b0825978 51show how you can use a method modifier to implement new behavior for a
52method.
9cf569f3 53
54The classes in the SYNOPSIS show two kinds of bank account. A simple
55bank account has one attribute, the balance, and two behaviors,
56depositing and withdrawing money.
57
58We then extend the basic bank account in the CheckingAccount
59class. This class adds another attribute, an overdraft account. It
60also adds overdraft protection to the withdraw method. If you try to
61withdraw more than you have, the checking account attempts to
62reconcile the difference by withdrawing money from the overdraft
63account. (1)
64
65The first class, B<BankAccount>, introduces a new attribute feature, a
66default value:
703d9522 67
68 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
69
9cf569f3 70This says that a B<BankAccount> has a C<balance> attribute, which has
71a C<Int> type constraint, a read/write accessor, and a default value
72of C<0>. This means that every instance of B<BankAccount> that is
73created will have its C<balance> slot initialized to C<0>, unless some
74other value is provided to the constructor.
703d9522 75
9cf569f3 76The C<deposit> and C<withdraw> methods should be fairly
77self-explanatory, as they are just plain old Perl 5 OO.
703d9522 78
9cf569f3 79As you know from the first recipe, the keyword C<extends> sets a
80class's superclass. Here we see that B<CheckingAccount> C<extends>
81B<BankAccount>. The next line introduces yet another new attribute
82feature, class-based type constraints:
703d9522 83
84 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
85
9cf569f3 86Up until now, we have only seen the C<Int> type constraint, which (as
87we saw in the first recipe) is a builtin type constraint. The
88C<BankAccount> type constraint is new, and was actually defined the
89moment we created the B<BankAccount> class itself. In fact, Moose
90creates a corresponding type constraint for every class in your
91program (2).
92
93This means that in the first recipe, constraints for both C<Point> and
94C<Point3D> were created. In this recipe, both C<BankAccount> and
95C<CheckingAccount> type constraints are created automatically. Moose
96does this as a convenience so that your classes and type constraint
97can be kept in sync with one another. In short, Moose makes sure that
98it will just DWIM (3).
99
100In B<CheckingAccount>, we see another method modifier, the C<before>
703d9522 101modifier.
102
103 before 'withdraw' => sub {
104 my ($self, $amount) = @_;
105 my $overdraft_amount = $amount - $self->balance();
e9bb8a31 106 if ($self->overdraft_account && $overdraft_amount > 0) {
703d9522 107 $self->overdraft_account->withdraw($overdraft_amount);
108 $self->deposit($overdraft_amount);
109 }
110 };
111
9cf569f3 112Just as with the C<after> modifier from the first recipe, Moose will
113handle calling the superclass method (in this case C<<
114BankAccount->withdraw >>).
703d9522 115
9cf569f3 116The C<before> modifier will (obviously) run I<before> the code from
117the superclass is run. Here, C<before> modifier implements overdraft
118protection by first checking if there are available funds in the
119checking account. If not (and if there is an overdraft account
120available), it transfers the amount needed into the checking
121account (4).
122
123As with the method modifier in the first recipe, we could use
124C<SUPER::> to get the same effect:
703d9522 125
126 sub withdraw {
127 my ($self, $amount) = @_;
128 my $overdraft_amount = $amount - $self->balance();
e9bb8a31 129 if ($self->overdraft_account && $overdraft_amount > 0) {
703d9522 130 $self->overdraft_account->withdraw($overdraft_amount);
131 $self->deposit($overdraft_amount);
132 }
133 $self->SUPER::withdraw($amount);
134 }
135
9cf569f3 136The benefit of taking the method modifier approach is we do not need
137to remember to call C<SUPER::withdraw> and pass it the C<$amount>
138argument when writing C<< CheckingAccount->withdraw >>.
139
140This is actually more than just a convenience for forgetful
141programmers. Using method modifiers helps isolate subclasses from
142changes in the superclasses. For instance, if B<<
143BankAccount->withdraw >> were to add an additional argument of some
144kind, the version of B<< CheckingAccount->withdraw >> which uses
145C<SUPER::withdraw> would not pass that extra argument correctly,
146whereas the method modifier version would automatically pass along all
147arguments correctly.
148
149Just as with the first recipe, object instantiation uses the C<new>
150method, which accepts named parameters.
151
152 my $savings_account = BankAccount->new( balance => 250 );
153
703d9522 154 my $checking_account = CheckingAccount->new(
9cf569f3 155 balance => 100,
156 overdraft_account => $savings_account,
157 );
703d9522 158
9cf569f3 159And as with the first recipe, a more in-depth example can be found in
160the F<t/000_recipes/basics/002_recipe.t> test file.
703d9522 161
162=head1 CONCLUSION
163
9cf569f3 164The aim of this recipe was to take the knowledge gained in the first
165recipe and expand upon it with a more realistic use case. The next
166recipe will expand on Moose attributes to create a behaviorally
167sophisticated class defined almost entirely by its attributes.
703d9522 168
169=head1 FOOTNOTES
170
171=over 4
172
173=item (1)
174
9cf569f3 175If you're paying close attention, you might realize that there's a
176circular loop waiting to happen here. A smarter example would have to
177make sure that we don't accidentally create a loop between the
178checking account and its overdraft account.
179
180=item (2)
181
182In reality, this creation is sensitive to the order in which modules
183are loaded. In more complicated cases, you may find that you need to
184explicitly declare a class type before the corresponding is loaded.
185
186=item (3)
187
188Moose does not attempt to encode a class's is-a relationships within
189the type constraint hierarchy. Instead, Moose just considers the class
190type constraint to be a subtype of C<Object>, and specializes the
191constraint check to allow for subclasses. This means that an instance
192of B<CheckingAccount> will pass a C<BankAccount> type constraint
193successfully. For more details, please refer to the
194L<Moose::Util::TypeConstraints> documentation.
195
196=item (4)
197
198If the overdraft account does not have the amount needed, it will
199throw an error. Of course, the overdraft account could also have
200overdraft protection. See note 1.
703d9522 201
202=back
203
204=head1 SEE ALSO
205
206=over 4
207
4711f5f7 208=item Acknowledgment
703d9522 209
9cf569f3 210The BankAccount example in this recipe is directly taken from the
c6182301 211examples in this chapter of "Practical Common Lisp":
703d9522 212
213L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
214
215=back
216
471c4f09 217=head1 AUTHOR
218
219Stevan Little E<lt>stevan@iinteractive.comE<gt>
220
221=head1 COPYRIGHT AND LICENSE
222
778db3ac 223Copyright 2006-2008 by Infinity Interactive, Inc.
471c4f09 224
225L<http://www.iinteractive.com>
226
227This library is free software; you can redistribute it and/or modify
228it under the same terms as Perl itself.
229
e08c54f5 230=cut