取势 明道 优术

作者为 扶 凯 发表

概要

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

 

描述

首先这个在演示中,告诉你怎么建一个基于 Moose 的类.重点在建立和操作相关属性上.在这个对象中是非常的面向数据的,所以并没有多少对象中方法的行为.在这个指南中,我详细叙述在前面一个指南中的概念和一些现实生活中的行为.特别地我们会在这演示告诉你看到,使用方法修饰创建新的对象方法的行为.
这个摘要的类中,代表着二种类型的银行帐号.一个银行账户有 balance(余额)属性, depositing (存钱) 和 withdrawing(取钱) 两个方法(行为).

我们给 CheckingAccount 类,通过前一个指南中讲过的 extend 关键字来扩展成 BankAccount 的子类. 这个类添加了另一个新的属性 overdraft_account(透支帐户).也在 withdraw(取钱)的类方法中添加了一个透支保护的方法.如果你试着取出,超过你自己所存的所有钱.这个 CheckingAccount 类中会从 overdraft_account 透支帐户中来取出钱的差价(1).

在类 BankAccount 中声明了一个 balance 的属性,这个加了一个新的属性特性 default.

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

上面这句,这是指 BankAccount 有一个 balance 的属性,这 has 中 isa 是用来检查传进来的属性内容的类型约束为 Int.和可以读写,默认值为 0 .这指出每当 BankAccount 类构建时会新建一个 balance 的属性并初始化默认值为 0 .除非有别的值传给这个类的构造器.

 

这个 deposit 存钱和 withdraw 取钱的方法,这个不需要讲了吧,只是简单的使用 Perl 5 的 OO 的功能(2).

在上一个指南中讲到关键字 extends 中可以设置一个类的父类. 在这个 CheckingAccount 类中是通过 extends 关键字扩展成的 BankAccount 的子类.在下面行中,又要推出作者的新的属性特性"基于类的类型约束":

has 'overdraft_account' => ( isa => 'BankAccount', is => 'rw' );

直到现在,我们只见到了 Int 的类型约束.这是一个 Moose 原生内置的类型约束.这个 isa 中的 BankAccount 是一个新的类型约束. 这个新的类型约束是在定义和创建 BankAccount 类时自动新建出来的.实事上,Moose 会为你的每一个程序中的类创建一个同名的类型约束(3).
这意味着,在上个指南中,有二个新的类型约束 Point 和 Point3D.现在这个例子中, BankAccount 和 CheckingAccount 的类型约束也会自动的创建.这是不是非常方便,因为同一个作者的类和类型约束总能保持同步.总之,Moose 会确保它 DWIM (4).

 

在 CheckingAccount 的类中,我们可以见到一个新的方法修饰 before,在方法调用之前的时候修改方法.

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

 这个功能很象上一个指南讲的,只是上次 after 的方法修饰是修改之后生效.在这 Moose 会调用父类的方法.(在这个例子中是 BankAccount->withdraw);
这个 before 的方法修饰会在父类调用 withdraw 之前来运行.这 before 的方法修饰在你 CheckingAccount(活期存款)类中创建了一个 overdraft 透支的保护功能.它会给出 CheckingAccount (活期存款)所需的金额(5).

与前面一个指南中的方法修饰一样,我们同样也能使用 SUPER:: 来到取得相同的结果…呵呵,因为这也是 Perl.

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

使用方法修饰的好处是,我们并不需要记住调用 SUPER::withdraw 和通过传 $amount 的参数给 CheckingAccount->withdraw .
这个功能对我们这种健忘的程序员真的是非常的方便.在这使用方法修饰可以帮助我们隔离从子类的父类的变化.从这个实例中,如果 BankAccount->withdraw 添加一个附加的一些参数.这个版本中 CheckingAccount->withdraw 使用 SUPER::withdraw 时就不能正常使用了.但使用方法修饰的版本的类还是能自动的并且正确的接收全部的参数.

就象前一个指南.对象的使用方法如下,一样是使用 new 的方法,可以使用定义过的属性名做参数来构造类;

my $savings_account = BankAccount->new( balance => 250 );
my $checking_account = CheckingAccount->new(
      balance           => 100,
      overdraft_account => $savings_account,
);

 也象前一个指南,更多的深入的例子,可以看 t/recipes/moose_cookbook_basics_recipe2.t 的测试文件.
这指南上增加了更多的“现实世界”的实际用例,并基于第一个指南的基本概念来做了一些扩展.

补充说明

(1) 如果你认真分析,你可能会意识到一个问题,在这会产生一个循环引用.一个更好的的例子就是要确保我们不会意外的在 checking account 类和overdraft account 类之间创建循环.
(2)注意这只是想到的一些简单的方法,只是操作一些单一的数据片段,所以正常时候没有必要这样.另外 deposit 功能,可以看看 Moose::Meta::Attribute::Native::Trait::Counter.
(3) 事实上,这种创建是建立在模块加载顺序上的.在更复杂的情况下,您可能会发现,你需要明确地声明一个类的类型,然后进行相应的类加载.
(4) Moose 不会尝试编码类的内在的层次关系的约束.相反,只是认为类的类型约束只是对象中的一个子类型,并会检查这个约束,以允许子类.这意味着,一个CheckingAccount 实例将通过一个 BankAccount 成功的进行类型约束.有关详细信息,请参阅 Moose::Util::TypeConstraints
(5)如果 overdraft 的帐户少了要取出的所必须的钱.它就会出错.因为透支帐户的透支保护.可以看看 note 1.

 

 

来了就留个评论吧! 2个评论