Inside Perl5 OO
対象読者: 他の言語で OO したことあるっぽいひと/Perl4 でとまってるひと
まずは点クラスをつくってみましょう
package Point;
sub new {
my ($class, %args) = @_; # 第1引数はクラス名
bless({%args}, $class); # データを bless すると、オブジェクトになる
}
sub x {
my $self = shift; # 第1引数は、インスタンス
$self->{x} = shift if @_; # 引数があればセッター扱いになる
$self->{x};
}
sub y {
my $self = shift;
$self->{y} = shift if @_;
$self->{y};
}
このようにして作ったクラスは、下記のようにして呼び出すことができます
my $p = Point->new(x => 1, y => 2);
$p->x(); # ゲッター
$p->x( 4 ); # セッター
簡単ですね。
実は、この Point->new は
my $p = new Point(x => 1, y => 2);
とも書くことができます。このとき new は予約語でもなんでもありません。なんでも好きなメソッド名を使えます。不思議な言語ですね。
ソースコードを Perl5 がどのように解釈しているのかを確認するには、B::Deparse というモジュールを使います。下記のように実行すると、解釈結果が見えるので便利です。
% perl -MO=Deparse,-P -e 'new Point()'
'Point'->new;
new が特別扱いされてないことも確認しましょう。
% perl -MO=Deparse,-P -e 'foo Point()'
'Point'->foo;
OK いい感じです。
package Point;
sub new { }
は実は、
sub Point::new { }
と同義です。
さらにもう一歩すすめると
Point->new(x => 1, y => 2)
は
Point::new('Point', x => 1, y => 2)
と同じ。
$p->x( 3 )
は
Point::x( $p, 3 )
と同じです。
実際、どっちでよんでも結果は変わりません。
Point クラスを継承した Point3D というクラスを作ることを考えます。Perl5 で継承を行うには、下記のようにすればいいでしょう。
package Point3D;
use base 'Point';
sub z {
my $self = shift;
$self->{z} = shift if @_;
$self->{z};
}
簡単ですね。
use base 親クラス名
と書くだけで、継承できます。しかしこれは
push @Point3D::ISA, 'Point';
と同じことです。
my $p = Point3D->new(x => 2);
$p->x();
と呼んだとき、perl5 は、Point3D::x() を探し、見つからなければ @Point3D::ISA の中にはいっているクラス名を順番に探索し、Point::x() にいきつくということになります。
Perl5 のオブジェクト指向サポートは、素材の段階だと私は考えています。このまま使うのはちょっと大変です。
そこで、なんとかしてくれる CPAN Module があるんです。
Class::Accessor::Fast を使うと
package Point;
use base qw/Class::Accessor::Fast/;
__PACKAGE__->mk_accessors('x', 'y');
これで、最初のコードと同じ機能をもつクラスをつくれます。
Moose を使うと、下記のように宣言的に書くことができます。
package Point;
use Moose;
has x => (is => 'rw');
has y => (is => 'rw');
Last modified: $Date: 2008-10-26T05:38:56.231052Z $