Perl-users.jp

Inside Perl5 OO

対象読者: 他の言語で OO したことあるっぽいひと/Perl4 でとまってるひと

Class をつくる

まずは点クラスをつくってみましょう

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

Class::Accessor::Fast を使うと

package Point;
use base qw/Class::Accessor::Fast/;
__PACKAGE__->mk_accessors('x', 'y');

これで、最初のコードと同じ機能をもつクラスをつくれます。

Moose

Moose を使うと、下記のように宣言的に書くことができます。

package Point;
use Moose;

has x => (is => 'rw');
has y => (is => 'rw');

目次へ

Last modified: $Date: 2008-10-26T05:38:56.231052Z $