Moose::Cookbook::Basics::BankAccount_MethodModifiersAndSubclassing - phpMan

Command: man perldoc info search(apropos)  


Sections
NAME VERSION SYNOPSIS DESCRIPTION CONCLUSION FOOTNOTES ACKNOWLEDGMENT AUTHORS COPYRIGHT AND LICENSE
NAME
    Moose::Cookbook::Basics::BankAccount_MethodModifiersAndSubclassing -
    Demonstrates the use of method modifiers in a subclass

VERSION
    version 2.2200

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);
          }
      };

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, BankAccount, introduces a new attribute feature, a
    default value:

      has 'balance' => ( isa => 'Int', is => 'rw', default => 0 );

    This says that a BankAccount has a "balance" attribute, which has an
    "Int" type constraint, a read/write accessor, and a default value of 0.
    This means that every instance of BankAccount that is created will have
    its "balance" slot initialized to 0, unless some other value is provided
    to the constructor.

    The "deposit" and "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 "extends" sets a class's
    superclass. Here we see that CheckingAccount "extends" 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 "Int" type constraint, which (as we
    saw in the first recipe) is a builtin type constraint. The "BankAccount"
    type constraint is new, and was actually defined the moment we created
    the 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 "Point" and
    "Point3D" were created. In this recipe, both "BankAccount" and
    "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 CheckingAccount, we see another method modifier, the "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 "after" modifier from the first recipe, Moose will
    handle calling the superclass method (in this case
    "BankAccount->withdraw").

    The "before" modifier will (obviously) run *before* the code from the
    superclass is run. Here, "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 "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 "SUPER::withdraw" and pass it the $amount argument when
    writing "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 BankAccount->withdraw were to add an
    additional argument of some kind, the version of
    CheckingAccount->withdraw which uses "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 "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 t/recipes/basics_bankaccount_methodmodifiersandsubclassing.t test
    file.

CONCLUSION
    This recipe expanded on the basic concepts from the first recipe with a
    more "real world" use case.

FOOTNOTES
    (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.

    (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, "deposit" could be implemented via the "inc"
        native delegation for counters - see
        Moose::Meta::Attribute::Native::Trait::Counter for more specifics,
        and Moose::Meta::Attribute::Native for a broader overview.

    (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.

    (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 "Object", and specializes
        the constraint check to allow for subclasses. This means that an
        instance of CheckingAccount will pass a "BankAccount" type
        constraint successfully. For more details, please refer to the
        Moose::Util::TypeConstraints documentation.

    (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.

ACKNOWLEDGMENT
    The BankAccount example in this recipe is directly taken from the
    examples in this chapter of "Practical Common Lisp":

    <http://www.gigamonkeys.com/book/object-reorientation-generic-functions.
    html>

AUTHORS
    *   Stevan Little <stevan AT cpan.org>

    *   Dave Rolsky <autarch AT urth.org>

    *   Jesse Luehrs <doy AT cpan.org>

    *   Shawn M Moore <sartak AT cpan.org>

    *   יובל קוג'מן (Yuval Kogman) <nothingmuch AT woobling.org>

    *   Karen Etheridge <ether AT cpan.org>

    *   Florian Ragwitz <rafl AT debian.org>

    *   Hans Dieter Pearcey <hdp AT cpan.org>

    *   Chris Prather <chris AT prather.org>

    *   Matt S Trout <mstrout AT cpan.org>

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.


Generated by phpMan Author: Che Dong On Apache Under GNU General Public License - MarkDown Format
2026-05-23 08:44 @216.73.217.24 CrawledBy Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
Valid XHTML 1.0 TransitionalValid CSS!

^_back to top