X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FCookbook%2FRecipe2.pod;h=e52c4c022ddc3e3db857356f1059ee835de1f5c4;hb=dec63b1ac3f931810b2bb917f49f0b3a158b712f;hp=ce8ca3b392efe9c72a66c896d5324f9e133d9df3;hpb=471c4f0999c20c70c19433519b2200171703cc6c;p=gitmo%2FMoose.git diff --git a/lib/Moose/Cookbook/Recipe2.pod b/lib/Moose/Cookbook/Recipe2.pod index ce8ca3b..e52c4c0 100644 --- a/lib/Moose/Cookbook/Recipe2.pod +++ b/lib/Moose/Cookbook/Recipe2.pod @@ -3,13 +3,11 @@ =head1 NAME -Moose::Cookbook::Recipe2 +Moose::Cookbook::Recipe2 - A simple B example =head1 SYNOPSIS package BankAccount; - use strict; - use warnings; use Moose; has 'balance' => (isa => 'Int', is => 'rw', default => 0); @@ -28,8 +26,6 @@ Moose::Cookbook::Recipe2 } package CheckingAccount; - use strict; - use warnings; use Moose; extends 'BankAccount'; @@ -37,27 +33,179 @@ Moose::Cookbook::Recipe2 has 'overdraft_account' => (isa => 'BankAccount', is => 'rw'); before 'withdraw' => sub { - my ($self, $amount) = @_; - my $overdraft_amount = $amount - $self->balance(); - if ($overdraft_amount > 0) { - $self->overdraft_account->withdraw($overdraft_amount); - $self->deposit($overdraft_amount); - } + my ($self, $amount) = @_; + my $overdraft_amount = $amount - $self->balance(); + if ($self->overdraft_account && $overdraft_amount > 0) { + $self->overdraft_account->withdraw($overdraft_amount); + $self->deposit($overdraft_amount); + } }; =head1 DESCRIPTION +In the first recipe we demonstrated the construction of basic +Moose classes whose attributes had various accessor schemes and +builtin type constraints. However, our objects were very data- +oriented, and did not have many behavioral aspects (i.e. methods) +to them. In this recipe, we will expand upon the concepts from +the first recipe and give a more realistic scenario of more +behavior oriented classes. + +We are using the example of a bank account, which has a standard +account (you can deposit money, withdraw money and check your +current balance), and a checking account which has optional +overdraft protection. The overdraft protection will protect the +owner of the checking account by automatically withdrawing the +needed funds from the overdraft account to ensure that a check +will not bounce. + +Now, onto the code. The first class, B, introduces a +new attribute feature: a default value. + + has 'balance' => (isa => 'Int', is => 'rw', default => 0); + +This tells us that a B has a C attribute, +which has the C type constraint, a read/write accessor, +and a default value of C<0>. This means that every instance of +B that is created will have its C slot +initialized to C<0>. Very simple really :) + +Next come the methods. The C and C methods +should be fairly self-explanatory; they are nothing specific to +Moose, just your standard Perl 5 OO. + +Now, onto the B class. As you know from the +first recipe, the keyword C sets a class's superclass +relationship. Here we see that B is a +B. The next line introduces yet another new aspect +of Moose, that of class-based type-constraints: + + has 'overdraft_account' => (isa => 'BankAccount', is => 'rw'); + +Up until now, we have only had C type constraints, which +(as I said in the first recipe) is a builtin type constraint +that Moose provides for you. The C type constraint +is new, and was actually defined the moment we created the +B class itself. In fact, for every Moose class that +you define, a corresponding type constraint will be created. This +means that in the first recipe, both C and C type +constraints were created, and in this recipe, both C +and C type constraints were created. Moose does +this as a convenience so that your class model and the type +constraint model can be kept in sync with one another. In short, +Moose makes sure that it will just DWIM (1). + +Next, we come to the behavioral part of B, and +again we see a method modifier, but this time it is a C +modifier. + + before 'withdraw' => sub { + my ($self, $amount) = @_; + my $overdraft_amount = $amount - $self->balance(); + if ($self->overdraft_account && $overdraft_amount > 0) { + $self->overdraft_account->withdraw($overdraft_amount); + $self->deposit($overdraft_amount); + } + }; + +Just as with the C modifier from the first recipe, Moose +will handle calling the superclass method (in this case the +C method). The C modifier shown +above will run (obviously) I the code from the superclass +with run. The C modifier here implements the overdraft +protection by first checking if there are enough available +funds in the checking account and if not (and if there is an overdraft +account available), it transfers the appropriate funds into the +checking account. + +As with the method modifier in the first recipe, there is another +way to accomplish this same thing using the built in C +pseudo-package. So the above method is equivalent to the one here. + + sub withdraw { + my ($self, $amount) = @_; + my $overdraft_amount = $amount - $self->balance(); + if ($self->overdraft_account && $overdraft_amount > 0) { + $self->overdraft_account->withdraw($overdraft_amount); + $self->deposit($overdraft_amount); + } + $self->SUPER::withdraw($amount); + } + +The benefits of taking the method modifier approach is that the +author of the B subclass does not need to remember +to call C and to pass it the C<$amount> argument. +Instead the method modifier ensures that all arguments make it +to the superclass method correctly. But this is actually more +than just a convenience for forgetful programmers, it also helps +isolate subclasses from changes in the superclasses. For instance, +if B were to add an additional argument +of some kind, the version of B which +uses C would not pass that extra argument +correctly, whereas the method modifier version would automatically +pass along all arguments correctly. + +Just as with the first recipe, object instantiation is a fairly +normal process, here is an example: + + my $savings_account = BankAccount->new(balance => 250); + my $checking_account = CheckingAccount->new( + balance => 100, + overdraft_account => $savings_account + ); + +And as with the first recipe, a more in-depth example of using +these classes can be found in the F test file. + +=head1 CONCLUSION + +The aim of this recipe was to take the knowledge gained in the +first recipe and expand upon it with a more realistic use case. I +hope that this recipe has accomplished this goal. The next recipe +will expand even more upon the capabilities of attributes in Moose +to create a behaviorally sophisticated class almost entirely +defined by attributes. + +=head1 FOOTNOTES + +=over 4 + +=item (1) + +Moose does not attempt to encode a class's is-a relationships +within the type constraint hierarchy. Instead, Moose just +considers the class type constraint to be a subtype of C, +and specializes the constraint check to allow for subclasses. This +means that an instance of B will pass a +C type constraint successfully. For more details, +please refer to the L documentation. + +=back + +=head1 SEE ALSO + +=over 4 + +=item Acknowledgment + +The BankAccount example in this recipe is directly taken from the +examples in this chapter of "Practical Common Lisp": + +L + +=back + =head1 AUTHOR Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Infinity Interactive, Inc. +Copyright 2006-2008 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. -=cut \ No newline at end of file +=cut