X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FCookbook%2FRecipe2.pod;h=bbd4788fdc78b99895157dd31e4919f96286880e;hb=43b50af34c88c00f35c97282cfcb8f5cd5bd81c4;hp=1786c1bb49558e93eceefe931ab98f21c7426d8c;hpb=cdcae9704d3a6e534204e50632abd26fde5530e1;p=gitmo%2FMoose.git diff --git a/lib/Moose/Cookbook/Recipe2.pod b/lib/Moose/Cookbook/Recipe2.pod index 1786c1b..bbd4788 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'; @@ -39,7 +35,7 @@ Moose::Cookbook::Recipe2 before 'withdraw' => sub { my ($self, $amount) = @_; my $overdraft_amount = $amount - $self->balance(); - if ($overdraft_amount > 0) { + if ($self->overdraft_account && $overdraft_amount > 0) { $self->overdraft_account->withdraw($overdraft_amount); $self->deposit($overdraft_amount); } @@ -47,17 +43,171 @@ Moose::Cookbook::Recipe2 =head1 DESCRIPTION +In the first recipe we showed how to build basic Moose classes +whose attributes had various accessor schemes and built in +type constraints. However our objects were very data-oriented, +and did not have many behavioral aspects to them (i.e. - methods). +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 an 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, that of 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 explanitory, 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 built-in type constraint +that Moose provides for you. The C type constraint +is new, and was actually defined at the moment we created the +B class itself. In fact, for every Moose class that +you define, a corresponding type constraint will be created for +that class. This means that in the first recipe, a C and +C type constraint were created, and in this recipe, both +a C and a C type constraint were +created. Moose does this as a convenience for you so that your +class model and the type constraint model can both 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 we have 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 assures 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 learned in the +first recipe and expand upon it within a more realistic use case. +I hope that this recipe has accomplished this goal. The next +recipe will expand even more upon the capabilties 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 Acknowledgement + +The BankAccount example in this recipe is directly taken from the +examples in this chapter of "Practical Common Lisp". A link to that +can be found here: + +L + +=back + =head1 AUTHOR Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE -Copyright 2006 by Infinity Interactive, Inc. +Copyright 2006, 2007 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