あるソフトウェアエンジニアの戯言

日々の開発やサーバ管理のなかで出てきたあれや、これや、あんなことや、こんなこと

この記事のキーワード:   Moose   perl

Moose::Cookbook::Basics::Recipe2
- 簡単な銀行口座の例

概要

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

例によって、転写はここまで。


気になったところ

この例で気になったのは、extends でも before でもなくて、BankAccount の withdraw メソッドの中にある confess という関数。
文脈から warn とか die とか croak みたいなもんだろうな、とは想像がつくのだけれど、実際に実行してみないことには、、、ということで、実行してみる。
withdraw に balance 以上の値をわたして実行してみると、以下のような出力が返ってきた。

Account overdrawn at BankAccount.pm line 14
BankAccount::withdraw('BankAccount=HASH(0x9c9ef50)', 101) called at BankAccount.pm line 30
Class::MOP::Class:::before('CheckingAccount=HASH(0x982a6e8)', 151) called at /usr/lib/perl5/Class/MOP/Method/Wrapped.pm line 47
Class::MOP::Method::Wrapped::__ANON__('CheckingAccount=HASH(0x982a6e8)', 151) called at /usr/lib/perl5/Class/MOP/Method/Wrapped.pm line 89
CheckingAccount::withdraw('CheckingAccount=HASH(0x982a6e8)', 151) called at moose.pl line 13

ようするに、バックトレース付きの croak ってことですね。おそらく Moose.pm からexport されている関数だろうし、APIリファレンスを当たれば説明がでているのだろうけど、今回は放置。。。。
FOOTNOTES の (4) を読むと、''throw an error'' とあるので、やはり croak と同様に die を実行するみたい。

あと、上の概要に示されているソースプログラムをmainプログラムの末尾にくっつけて実行してみたら、

Can't locate object method "balance" via package "BankAccount" at moose.pl line 27.

といわれてエラーになってしまった。(''moose.pl line 27'' はちょうど ''$self->balance($self->balance + $amount'' の行)
BankAccount.pm って別ファイルに保存して use BankAccount; すれば正常に動作したので、なにかがマズかったんだろうけれど、なにがマズかったのかまでは未調査。
FOOTNOTES の (2) で クラスが使用される前にロードされていなければならない、みたいなことが書いてあるので、コンパイルされる順序の問題のようだ。
ようするに、該当するクラスを new する前に use するなり、定義してやるなりしないといけないということのよう。

その他のハイライト

  • has のオプションで isa に 'Int', default に 0 を設定している (BankAccount の ''balance'' attribute)。コンストラクタに引数として渡されない限り、この初期値で初期化される。
  • has のオプションで isa の値にクラス名が使える。(CheckingAccount の ''overdraft_account'')
  • スーパークラスのメソッドの実行前に実行される ''before'' method modifier。 SUPER::method_name(args) を使ってもほぼ同じことが実現可能だが、スーパークラス側の引数が追加された場合などに追従するのが大変になる。
  • isa=>'BankAccount' と指定されているときに'CheckingAccount'はBankAccountのサブクラスなので、代入可能である。(通常のオブジェクト指向と同じ)
[トラックバック] トラックバックPingURL : http://blog.d-pad.co.jp/sugi/dblog/ping.fcgi/31
言及リンクのないページからのトラックバックは拒否される仕様になっておりますのでご注意ください。
名前:
メールアドレス(任意):
参考URL(任意):

キーワードリスト

バックナンバー