このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
property モジュールはコードリファレンスを使って、
該当パッケージのメソッドを関数のように呼び出す。
これは、継承の問題と関連し、以下の問題を生じる。
1. 継承したメソッドをプロパティ化できない
あるクラスを継承したクラスを作る場合、
祖先クラスに get_XXX/set_XXX のメソッドがあると、
継承したクラスはそれらを利用できる。
しかし、それらをオーバーライドしない限り、
メソッドの実体は祖先クラスにあり、
継承クラスにはメソッドの定義自体が存在しない。
つまり、継承クラスのシンボルテーブルには
get_XXX/set_XXX は存在しない。
そのため、継承したクラスが祖先クラスのメソッドを用いて
プロパティを定義するためには、
祖先クラスのメソッドを property に引き渡す必要がある。
use property
'name' => 'value',
'set' => \&<実装パッケージ>::set_value,
'get' => \&<実装パッケージ>::get_value;
しかし、通常は set_XXX/get_XXX の実際が、
どこで定義されてるかなどは分からない。
継承のどこかでオーバーライドされている可能性もある。
つまり、これは継承したクラスの作成者が、
メソッドがどこで定義されているか知っている必要がある。
オブジェクト指向的に、こういう設計は許されない。
2. プロパティをオーバーライドできない
祖先クラスが property モジュールを使って
プロパティを実装していた場合、
そのクラスには新しい lvalue メソッドが追加される。
このクラスを継承して新しいクラスを作った場合、
祖先クラスにある lvalue メソッドも継承され、
継承したクラスはそれらを利用することができる。
しかし、継承した lvalue メソッドは、
set_XXX/get_XXX を関数として呼び出すため、
set_XXX/get_XXX を継承クラスでオーバーライドしても、
lvalue メソッド経由では、祖先クラスのメソッドが呼ばれ、
オーバーライドしたメソッドが呼ばれないことになる。
1 番は明確だが、2 番は少し分かりにくいため、
実際にコードを書いて検証してみよう。
package Base;
use property
'name' => 'value',
'get' => \&get_value,
'set' => \&set_value;
sub new {
my $package = shift;
my $value = shift;
bless({'value' => $value}, $package);
}
sub get_value {
my $object = shift;
$object->{'value'};
}
sub set_value {
my $object = shift;
my $value = shift;
$object->{'value'} = $value;
}
package Derived;
use Carp;
our @ISA = ('Base');
sub set_value {
my $object = shift;
my $value = shift;
croak "Can't accept negative value." if $value < 0;
SUPER->set_value($value);
}
package main;
my $obj = Derived->new(3000);
print "Value: ", $obj->get_value, "\n";
$obj->value = -50;
print "Value: ", $obj->value, "\n";
$obj->set_value(-50);
print "Value: ", $obj->value, "\n";
Base は、set_value と、get_value を定義し、
property モジュールを使って、value プロパティを作る。
これらは単純に値を設定したり返したりするだけだ。
Derived は Base を継承し、set_value をオーバーライド。
set_value は、設定値を確認し、負の値を拒否する。
そうでなければ、親クラスの set_value を呼び出す。
set_value/get_value は、メソッドとして呼び出すので、
Perl の継承のメカニズムによって処理される。
そのため、$obj->set_value は、Derived で処理され、
$obj->get_value は、Base で処理されてうまくいく。
しかし、$obj->value = -50 としたときは問題が起きる。
「value」の呼び出しは、継承が考慮されるので、
Base の value メソッドが呼び出される。
value メソッドへの代入は、マジカルスカラの機能により、
「Base::set_value」への呼び出しに転送される。
コードリファレンス経由であるため、
「Derived::set_value」は呼び出されないのだ。
そのため、$obj->value = -50; の呼び出しは、
croak せずに成功してしまう。