このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
では、property モジュールに実装を作っていこう。
まずは、use の呼び出しの規約を考える。
プロパティの作成に必要なのは、
名前、setter メソッド, getter メソッドの三つ。
それをハッシュで渡すようにしよう。
use property 'name' => 'salary',
'get' => \&get_salary,
'set' => \&set_salary;
========== property.pm ==========
package property;
use v5.6.0;
use strict;
use warnings;
BEGIN {
our $Version = '0.01';
}
sub import {
my $package = shift;
my %param = @_;
my $target = caller;
_create_property($target, $param{'name'},
$param{'get'}, $param{'set'});
}
sub _create_property {
my ($package, $name, $get, $set) = @_;
my $propertizer = sub : lvalue {
my $this = shift;
tie(my $reflector, __PACKAGE__, $this, $get, $set);
$reflector;
};
_register_code($package, $name, $propertizer);
}
sub _register_code {
my ($package, $name, $code) = @_;
my $symbol = "${package}::${name}";
no strict 'refs';
*$symbol = $code;
}
sub TIESCALAR {
my ($package, $object, $get, $set) = @_;
$package = (ref $package or $package or __PACKAGE__);
my $this = { 'object' => $object, 'get' => $get, 'set' => $set };
bless($this, $package);
}
sub FETCH {
my $this = $_[0];
$this->{'get'}->($this->{'object'});
}
sub STORE {
my ($this, $value) = @_;
$this->{'set'}->($this->{'object'}, $value);
}
1;
========== end of property.pm ==========
import メソッドは、use の時に呼ばれる。
property モジュールは、'name' で示された
プロパティを lvalue メソッドとして追加する。
これにはシンボリックリファレンスを利用するのだ。
これは _register_code ルーチンで行なっている。
登録する lvalue メソッドは sub を使って動的に作成する。
これは _create_property ルーチンで行なっている。
面白いのは、sub の内部から、その外側にあるはずの、
引数として渡されているパラメータを参照してることだ。
このような無名関数をクロージャと呼ぶ。
クロージャはこれらへの参照を保持するので、
_create_property の外に外れても問題なく使える。
クロージャは、lvalue ルーチンであり、
tie されたマジカルなスカラを返却する。
tie の裏にあるオブジェクトは、
独立したパッケージにしても良かったが、
property 自体を利用することにしよう。
TIESCALAR で作成するオブジェクトには、
'get' と 'set' への参照を保持し、
FETCH, STORE のときにそれらを呼び出すのだ。