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