Build.PL - need Class::MOP 0.31
[gitmo/Moose.git] / lib / Moose / Cookbook / Recipe2.pod
CommitLineData
471c4f09 1
2=pod
3
4=head1 NAME
5
3824830b 6Moose::Cookbook::Recipe2 - A simple B<BankAccount> example
471c4f09 7
8=head1 SYNOPSIS
9
10 package BankAccount;
11 use strict;
12 use warnings;
13 use Moose;
14
15 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
16
17 sub deposit {
18 my ($self, $amount) = @_;
19 $self->balance($self->balance + $amount);
20 }
21
22 sub withdraw {
23 my ($self, $amount) = @_;
24 my $current_balance = $self->balance();
25 ($current_balance >= $amount)
26 || confess "Account overdrawn";
27 $self->balance($current_balance - $amount);
28 }
29
30 package CheckingAccount;
31 use strict;
32 use warnings;
33 use Moose;
34
35 extends 'BankAccount';
36
37 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
38
39 before 'withdraw' => sub {
cdcae970 40 my ($self, $amount) = @_;
41 my $overdraft_amount = $amount - $self->balance();
e9bb8a31 42 if ($self->overdraft_account && $overdraft_amount > 0) {
cdcae970 43 $self->overdraft_account->withdraw($overdraft_amount);
44 $self->deposit($overdraft_amount);
45 }
471c4f09 46 };
47
48=head1 DESCRIPTION
49
703d9522 50In the first recipe we showed how to build basic Moose classes
51whose attributes had various accessor schemes and built in
52type constraints. However our objects were very data-oriented,
53and did not have many behavioral aspects to them (i.e. - methods).
54In this recipe, we will expand upon the concepts from the first
55recipe and give a more realistic scenario of more behavior
56oriented classes.
57
58We are using an example of a bank account, which has a standard
59account (you can deposit money, withdraw money and check your
60current balance), and a checking account which has optional
61overdraft protection. The overdraft protection will protect the
62owner of the checking account by automatically withdrawing the
63needed funds from the overdraft account to ensure that a check
64will not bounce.
65
66Now, onto the code. The first class B<BankAccount> introduces a
67new attribute feature, that of a default value.
68
69 has 'balance' => (isa => 'Int', is => 'rw', default => 0);
70
71This tells is that a B<BankAccount> has a C<balance> attribute,
72which is has the C<Int> type constraint, a read/write accessor,
73and a default value of C<0>. This means that every instance of
74B<BankAccount> that is created will have it's C<balance> slot
75initialized to C<0>. Very simple really :)
76
77Next come the methods. The C<deposit> and C<withdraw> methods
78should be fairly self explanitory, they are nothing specific to
79Moose, just your standard Perl 5 OO.
80
81Now, onto the B<CheckingAccount> class. As you know from the
82first recipe, the keyword C<extends> sets a class's superclass
83relationship. Here we see that B<CheckingAccount> is a
84B<BankAccount>. The next line introduces yet another new aspect
85of Moose, that of class based type-constraints.
86
87 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');
88
89Up until now, we have only had C<Int> type constraints, which
90(as I said in the first recipe) is a built-in type constraint
91that Moose provides for you. The C<BankAccount> type constraint
92is new, and was actually defined at the moment we created the
93B<BankAccount> class itself. In fact, for every Moose class that
94you define, a corresponding type constraint will be created for
95that class. This means that in the first recipe, a C<Point> and
96C<Point3D> type constraint were created, and in this recipe, both
97a C<BankAccount> and a C<CheckingAccount> type constraint were
98created. Moose does this as a convience for you so that your
99class model and the type constraint model can both be kept in
100sync with one another. In short, Moose makes sure that it will
101just DWIM (1).
102
103Next, we come to the behavioral part of B<CheckingAccount>, and
104again we see a method modifier, but this time we have a C<before>
105modifier.
106
107 before 'withdraw' => sub {
108 my ($self, $amount) = @_;
109 my $overdraft_amount = $amount - $self->balance();
e9bb8a31 110 if ($self->overdraft_account && $overdraft_amount > 0) {
703d9522 111 $self->overdraft_account->withdraw($overdraft_amount);
112 $self->deposit($overdraft_amount);
113 }
114 };
115
116Just as with the C<after> modifier from the first recipe, Moose
117will handle calling the superclass method (in this case the
118C<BankAccount::withdraw> method). The C<before> modifier shown
119above will run (obviously) I<before> the code from the superclass
120with run. The C<before> modifier here implements the overdraft
121protection by first checking if there are enough available
122funds in the checking account and if not (and if there is an overdraft
123account available), it transfers the appropriate funds into the
124checking account.
125
126As with the method modifier in the first recipe, there is another
127way to accomplish this same thing using the built in C<SUPER::>
128pseudo-package. So the above method is equivalent to the one here.
129
130 sub withdraw {
131 my ($self, $amount) = @_;
132 my $overdraft_amount = $amount - $self->balance();
e9bb8a31 133 if ($self->overdraft_account && $overdraft_amount > 0) {
703d9522 134 $self->overdraft_account->withdraw($overdraft_amount);
135 $self->deposit($overdraft_amount);
136 }
137 $self->SUPER::withdraw($amount);
138 }
139
140The benefits of taking the method modifier approach is that the
141author of the B<BankAccount> subclass does not need to remember
142to call C<SUPER::withdraw> and to pass it the C<$amount> argument.
143Instead the method modifier assures that all arguments make it
144to the superclass method correctly. But this is actually more
145than just a convience for forgetful programmers, it also helps
146isolate subclasses from changes in the superclasses. For instance,
147if B<BankAccount::withdraw> were to add an additional argument
148of some kind, the version of B<CheckingAccount::withdraw> which
149uses C<SUPER::withdraw> would not pass that extra argument
150correctly. Whereas the method modifier version of would pass
151all arguments along correctly automatically.
152
153Just as with the first recipe, object instantiation is a fairly
154normal process, here is an example:
155
156 my $savings_account = BankAccount->new(balance => 250);
157 my $checking_account = CheckingAccount->new(
158 balance => 100,
159 overdraft_account => $savings_account
160 );
161
162And as with the first recipe, a more in-depth example of using
db1ab48d 163these classes can be found in the F<t/002_recipe.t> test file.
703d9522 164
165=head1 CONCLUSION
166
167The aim of this recipe was to take the knowledge learned in the
168first recipe and expand upon it within a more realistic use case.
169I hope that this recipe has accomplished this goal. The next
170recipe will expand even more upon the capabilties of attributes
171in Moose to create a behaviorally sophisticated class almost
172entirely defined by attributes.
173
174=head1 FOOTNOTES
175
176=over 4
177
178=item (1)
179
180Moose does not attempt to encode a class's is-a relationships
181within the type constraint hierarchy. Instead Moose just considers
182the class type constraint to be a subtype of C<Object>, and
183specializes the constraint check to allow for subclasses. This
184means that an instance of B<CheckingAccount> will pass a
185C<BankAccount> type constraint successfully. For more details,
186please refer to the L<Moose::Util::TypeConstraints> documentation.
187
188=back
189
190=head1 SEE ALSO
191
192=over 4
193
194=item Acknowledgement
195
196The BankAccount example in this recipe is directly taken from the
197examples in this chapter of "Practical Common Lisp". A link to that
198can be found here:
199
200L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
201
202=back
203
471c4f09 204=head1 AUTHOR
205
206Stevan Little E<lt>stevan@iinteractive.comE<gt>
207
208=head1 COPYRIGHT AND LICENSE
209
210Copyright 2006 by Infinity Interactive, Inc.
211
212L<http://www.iinteractive.com>
213
214This library is free software; you can redistribute it and/or modify
215it under the same terms as Perl itself.
216
217=cut