summaryrefslogtreecommitdiff
path: root/lib/Moose/Cookbook/Basics/BankAccount_MethodModifiersAndSubclassing.pod
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-06-06 17:50:16 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-06-06 17:50:16 +0000
commit5ac2026f7eed78958d69d051e7a8e993dcf51205 (patch)
tree298c3d2f08bdfe5689998b11892d72a897985be1 /lib/Moose/Cookbook/Basics/BankAccount_MethodModifiersAndSubclassing.pod
downloadMoose-tarball-master.tar.gz
Diffstat (limited to 'lib/Moose/Cookbook/Basics/BankAccount_MethodModifiersAndSubclassing.pod')
-rw-r--r--lib/Moose/Cookbook/Basics/BankAccount_MethodModifiersAndSubclassing.pod384
1 files changed, 384 insertions, 0 deletions
diff --git a/lib/Moose/Cookbook/Basics/BankAccount_MethodModifiersAndSubclassing.pod b/lib/Moose/Cookbook/Basics/BankAccount_MethodModifiersAndSubclassing.pod
new file mode 100644
index 0000000..f4874e4
--- /dev/null
+++ b/lib/Moose/Cookbook/Basics/BankAccount_MethodModifiersAndSubclassing.pod
@@ -0,0 +1,384 @@
+# PODNAME: Moose::Cookbook::Basics::BankAccount_MethodModifiersAndSubclassing
+# ABSTRACT: Demonstrates the use of method modifiers in a subclass
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Moose::Cookbook::Basics::BankAccount_MethodModifiersAndSubclassing - Demonstrates the use of method modifiers in a subclass
+
+=head1 VERSION
+
+version 2.1405
+
+=head1 SYNOPSIS
+
+ package BankAccount;
+ use Moose;
+
+ has 'balance' => ( isa => 'Int', is => 'rw', default => 0 );
+
+ sub deposit {
+ my ( $self, $amount ) = @_;
+ $self->balance( $self->balance + $amount );
+ }
+
+ sub withdraw {
+ my ( $self, $amount ) = @_;
+ my $current_balance = $self->balance();
+ ( $current_balance >= $amount )
+ || confess "Account overdrawn";
+ $self->balance( $current_balance - $amount );
+ }
+
+ package CheckingAccount;
+ use Moose;
+
+ extends 'BankAccount';
+
+ has 'overdraft_account' => ( isa => 'BankAccount', is => 'rw' );
+
+ 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);
+ }
+ };
+
+=head1 DESCRIPTION
+
+The first recipe demonstrated how to build very basic Moose classes,
+focusing on creating and manipulating attributes. The objects in that
+recipe were very data-oriented, and did not have much in the way of
+behavior (i.e. methods). In this recipe, we expand upon the concepts
+from the first recipe to include some real behavior. In particular, we
+show how you can use a method modifier to implement new behavior for a
+method.
+
+The classes in the SYNOPSIS show two kinds of bank account. A simple
+bank account has one attribute, the balance, and two behaviors,
+depositing and withdrawing money.
+
+We then extend the basic bank account in the CheckingAccount
+class. This class adds another attribute, an overdraft account. It
+also adds overdraft protection to the withdraw method. If you try to
+withdraw more than you have, the checking account attempts to
+reconcile the difference by withdrawing money from the overdraft
+account. (1)
+
+The first class, B<BankAccount>, introduces a new attribute feature, a
+default value:
+
+ has 'balance' => ( isa => 'Int', is => 'rw', default => 0 );
+
+This says that a B<BankAccount> has a C<balance> attribute, which has
+an C<Int> type constraint, a read/write accessor, and a default value
+of C<0>. This means that every instance of B<BankAccount> that is
+created will have its C<balance> slot initialized to C<0>, unless some
+other value is provided to the constructor.
+
+The C<deposit> and C<withdraw> methods should be fairly
+self-explanatory, as they are just plain old Perl 5 OO. (2)
+
+As you know from the first recipe, the keyword C<extends> sets a
+class's superclass. Here we see that B<CheckingAccount> C<extends>
+B<BankAccount>. The next line introduces yet another new attribute
+feature, class-based type constraints:
+
+ has 'overdraft_account' => ( isa => 'BankAccount', is => 'rw' );
+
+Up until now, we have only seen the C<Int> type constraint, which (as
+we saw in the first recipe) is a builtin type constraint. The
+C<BankAccount> type constraint is new, and was actually defined the
+moment we created the B<BankAccount> class itself. In fact, Moose
+creates a corresponding type constraint for every class in your
+program (3).
+
+This means that in the first recipe, constraints for both C<Point> and
+C<Point3D> were created. In this recipe, both C<BankAccount> and
+C<CheckingAccount> type constraints are created automatically. Moose
+does this as a convenience so that your classes and type constraint
+can be kept in sync with one another. In short, Moose makes sure that
+it will just DWIM (4).
+
+In B<CheckingAccount>, we see another method modifier, the C<before>
+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<after> modifier from the first recipe, Moose will
+handle calling the superclass method (in this case C<<
+BankAccount->withdraw >>).
+
+The C<before> modifier will (obviously) run I<before> the code from
+the superclass is run. Here, C<before> modifier implements overdraft
+protection by first checking if there are available funds in the
+checking account. If not (and if there is an overdraft account
+available), it transfers the amount needed into the checking
+account (5).
+
+As with the method modifier in the first recipe, we could use
+C<SUPER::> to get the same effect:
+
+ 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 benefit of taking the method modifier approach is we do not need
+to remember to call C<SUPER::withdraw> and pass it the C<$amount>
+argument when writing C<< CheckingAccount->withdraw >>.
+
+This is actually more than just a convenience for forgetful
+programmers. Using method modifiers helps isolate subclasses from
+changes in the superclasses. For instance, if B<<
+BankAccount->withdraw >> were to add an additional argument of some
+kind, the version of B<< CheckingAccount->withdraw >> which uses
+C<SUPER::withdraw> 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 uses the C<new>
+method, which accepts named parameters.
+
+ 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 can be found in
+the F<t/recipes/moose_cookbook_basics_recipe2.t> test file.
+
+=head1 CONCLUSION
+
+This recipe expanded on the basic concepts from the first recipe with
+a more "real world" use case.
+
+=head1 FOOTNOTES
+
+=over 4
+
+=item (1)
+
+If you're paying close attention, you might realize that there's a
+circular loop waiting to happen here. A smarter example would have to
+make sure that we don't accidentally create a loop between the
+checking account and its overdraft account.
+
+=item (2)
+
+Note that for simple methods like these, which just manipulate some
+single piece of data, it is often not necessary to write them at all.
+For instance, C<deposit> could be implemented via the C<inc> native
+delegation for counters - see
+L<Moose::Meta::Attribute::Native::Trait::Counter> for more specifics,
+and L<Moose::Meta::Attribute::Native> for a broader overview.
+
+=item (3)
+
+In reality, this creation is sensitive to the order in which modules
+are loaded. In more complicated cases, you may find that you need to
+explicitly declare a class type before the corresponding class is
+loaded.
+
+=item (4)
+
+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<Object>, and specializes the
+constraint check to allow for subclasses. This means that an instance
+of B<CheckingAccount> will pass a C<BankAccount> type constraint
+successfully. For more details, please refer to the
+L<Moose::Util::TypeConstraints> documentation.
+
+=item (5)
+
+If the overdraft account does not have the amount needed, it will
+throw an error. Of course, the overdraft account could also have
+overdraft protection. See note 1.
+
+=back
+
+=head1 ACKNOWLEDGMENT
+
+The BankAccount example in this recipe is directly taken from the
+examples in this chapter of "Practical Common Lisp":
+
+L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
+
+=begin testing
+
+my $savings_account;
+
+{
+ $savings_account = BankAccount->new( balance => 250 );
+ isa_ok( $savings_account, 'BankAccount' );
+
+ is( $savings_account->balance, 250, '... got the right savings balance' );
+ is(
+ exception {
+ $savings_account->withdraw(50);
+ },
+ undef,
+ '... withdrew from savings successfully'
+ );
+ is( $savings_account->balance, 200,
+ '... got the right savings balance after withdrawal' );
+
+ $savings_account->deposit(150);
+ is( $savings_account->balance, 350,
+ '... got the right savings balance after deposit' );
+}
+
+{
+ my $checking_account = CheckingAccount->new(
+ balance => 100,
+ overdraft_account => $savings_account
+ );
+ isa_ok( $checking_account, 'CheckingAccount' );
+ isa_ok( $checking_account, 'BankAccount' );
+
+ is( $checking_account->overdraft_account, $savings_account,
+ '... got the right overdraft account' );
+
+ is( $checking_account->balance, 100,
+ '... got the right checkings balance' );
+
+ is(
+ exception {
+ $checking_account->withdraw(50);
+ },
+ undef,
+ '... withdrew from checking successfully'
+ );
+ is( $checking_account->balance, 50,
+ '... got the right checkings balance after withdrawal' );
+ is( $savings_account->balance, 350,
+ '... got the right savings balance after checking withdrawal (no overdraft)'
+ );
+
+ is(
+ exception {
+ $checking_account->withdraw(200);
+ },
+ undef,
+ '... withdrew from checking successfully'
+ );
+ is( $checking_account->balance, 0,
+ '... got the right checkings balance after withdrawal' );
+ is( $savings_account->balance, 200,
+ '... got the right savings balance after overdraft withdrawal' );
+}
+
+{
+ my $checking_account = CheckingAccount->new(
+ balance => 100
+
+ # no overdraft account
+ );
+ isa_ok( $checking_account, 'CheckingAccount' );
+ isa_ok( $checking_account, 'BankAccount' );
+
+ is( $checking_account->overdraft_account, undef,
+ '... no overdraft account' );
+
+ is( $checking_account->balance, 100,
+ '... got the right checkings balance' );
+
+ is(
+ exception {
+ $checking_account->withdraw(50);
+ },
+ undef,
+ '... withdrew from checking successfully'
+ );
+ is( $checking_account->balance, 50,
+ '... got the right checkings balance after withdrawal' );
+
+ isnt(
+ exception {
+ $checking_account->withdraw(200);
+ },
+ undef,
+ '... withdrawal failed due to attempted overdraft'
+ );
+ is( $checking_account->balance, 50,
+ '... got the right checkings balance after withdrawal failure' );
+}
+
+=end testing
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Stevan Little <stevan.little@iinteractive.com>
+
+=item *
+
+Dave Rolsky <autarch@urth.org>
+
+=item *
+
+Jesse Luehrs <doy@tozt.net>
+
+=item *
+
+Shawn M Moore <code@sartak.org>
+
+=item *
+
+יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
+
+=item *
+
+Karen Etheridge <ether@cpan.org>
+
+=item *
+
+Florian Ragwitz <rafl@debian.org>
+
+=item *
+
+Hans Dieter Pearcey <hdp@weftsoar.net>
+
+=item *
+
+Chris Prather <chris@prather.org>
+
+=item *
+
+Matt S Trout <mst@shadowcat.co.uk>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2006 by Infinity Interactive, Inc..
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut