=encoding utf8 =pod =head1 題名 Moose::Cookbook::Basics::Recipe1 - (毎度おなじみ) Bの例 =head1 概要 package Point; use Moose; has 'x' => (isa => 'Int', is => 'rw', required => 1); has 'y' => (isa => 'Int', is => 'rw', required => 1); sub clear { my $self = shift; $self->x(0); $self->y(0); } package Point3D; use Moose; extends 'Point'; has 'z' => (isa => 'Int', is => 'rw', required => 1); after 'clear' => sub { my $self = shift; $self->z(0); }; package main; # hash or hashrefs are ok for the constructor my $point1 = Point->new(x => 5, y => 7); my $point2 = Point->new({x => 5, y => 7}); my $point3d = Point3D->new(x => 5, y => 42, z => -5); =head1 本文 これは古典的なPointの例です。Perl 6の黙示録12番からそのまま引用してきました。K&Rの古典的なCの教科書にも同じような例が載っています。 Perl 5のクラスはすべてそうですが、Mooseのクラスもパッケージの中で定義されます。CとCはMooseの方で有効にしてくれますので、Cとさえ書いておけばうっかりミスを防げます。 Mooseはロードされると呼び出し元のパッケージ内にシュガー関数をいくつかエクスポートします(つまり、私たちはMooseの「キーワード」になる関数をいくつかインポートします)。もっとも、これらの関数は本当にPerl 5のキーワードになるわけではありません。あくまでも私たちのパッケージにエクスポートされたPerlの関数にすぎないものです。 Mooseは自動的に私たちのパッケージをLのサブクラスにします。Lクラスは、アトリビュートに対応したコンストラクタなど、さまざまな機能を提供してくれるものです。詳しくはLをご覧ください。 続いてはキーワードについてです。ここで紹介する最初のキーワードはCです。これはクラスの中でインスタンスのアトリビュートを定義するものです。 has 'x' => (isa => 'Int', is => 'rw', required => 1); このようにすると、Cという名前のアトリビュートが生成されます。Cパラメータは、このアトリビュートに保存される値はC型の制約を満たすことを期待している、という意味です(1)。このアトリビュート用に生成されるアクセサは読み書き可能になります。 C<< requires => 1 >>というパラメータは、新しいオブジェクトを生成するときにはかならずこのアトリビュートを用意しなければならないという意味です(座標情報のないpointオブジェクトはあまり意味があるとは思えないので許していません)。 アトリビュートを定義したので、今度はメソッドを定義しましょう。Mooseの場合も、ふつうのPerl 5のオブジェクト指向と同じく、メソッドはパッケージ内で定義されたサブルーチンにすぎません。 sub clear { my $self = shift; $self->x(0); $self->y(0); } これでBクラスはおしまいです。 続いて、BのサブクラスであるBを作りましょう。スーパークラスを宣言するにはMooseのキーワードであるCを使います。 extends 'Point'; このCキーワードはCによく似て、最初に必要があればクラスをロードするのですが、Cと違って、CキーワードはパッケージのC<@ISA>にどんな値が含まれていても「上書き」してしまいます(CはパッケージのC<@ISA>に値を追加します)。 個人的には、Cの振る舞いの方が直感的だと思います。(2) 次に、BにCという新しいアトリビュートを作ります。 has 'z' => (isa => 'Int', is => 'rw', required => 1); このアトリビュートはBのC、Cアトリビュートと同じようなものです。 Cキーワードは、Mooseでは「メソッドモディファイア」(アスペクト指向プログラミングでいう「アドバイス」)と呼ばれる機能です。 after 'clear' => sub { my $self = shift; $self->z(0); }; BオブジェクトでCが呼ばれると、モディファイアメソッドも呼ばれます(当然ながら、モディファイアが呼ばれるのは本物のメソッドの「あと」になります)。 この例では、本物のCメソッドはBから継承したものです。モディファイアメソッドは修飾されるメソッドと同じ引数を受け取ります(この場合はC<$self>のみです)。 もちろんCモディファイアを使うのが唯一のやり方ではありません。B<これはPerlですから>ね。このようなコードでも同じ結果が得られます。 sub clear { my $self = shift; $self->SUPER::clear(); $self->z(0); } また、Cという別のモディファイアを使うこともできるでしょう。 override 'clear' => sub { my $self = shift; super(); $self->z(0); }; Cモディファイアを使うとCというキーワードを利用できるようになります。これは非常にRuby的なやり方でスーパークラスのメソッドにディスパッチするためのものです。 メソッドモディファイアを使うかどうか、またどのモディファイアを使うかどうかの選択は、往々にして機能の問題であるのと同じくらいスタイルの問題でもあります。 BはLを継承しているので、Lのデフォルトコンストラクタも継承します。 my $point1 = Point->new(x => 5, y => 7); my $point2 = Point->new({x => 5, y => 7}); my $point3d = Point3D->new(x => 5, y => 42, z => -5); Cコンストラクタはこのクラスで定義されている各アトリビュートの名前付き引数のペアを、ハッシュまたはハッシュリファレンスの形で受け付けます。この例の場合、アトリビュートは必須ですから、引数を渡さずにCを呼ぶとエラーになります。 my $point = Point->new( x => 5 ); # no y, kaboom! これで、C<$point>とC<$point3d>はほかのPerl 5オブジェクトと同様に使えるようになりました。どんなことができるかをもっと詳しく知りたい方はFのテストファイルをご覧ください。 =head2 Mooseオブジェクトはハッシュリファレンスにすぎません ここまで紹介してきたことはいずれもいささか魔法じみて見えるかもしれませんが、大事なのは、Mooseのオブジェクトはフードをかぶったハッシュリファレンスにすぎない、ということ(3)。たとえば、C<$self>をCに渡してやれば、まさに期待通りの出力が得られます。 オブジェクトのデータ構造の中をつつき回すことさえできます(まったくおすすめできませんが)。 Mooseのオブジェクトがハッシュリファレンスであるということは、Mooseを使っていないクラスであってもハッシュリファレンスでさえあれば簡単にMooseで拡張できるということ。ハッシュリファレンス以外のクラスを拡張したい場合は、Cを試してみてください。 =head1 まとめ このレシピではいくつかの基本的なコンセプト(アトリビュート、サブクラス、簡単なメソッドモディファイア)について説明しました。 =head1 脚注 =over 4 =item (1) Mooseには最初から多くの型制約が用意されています(Cもそのひとつです)。型制約システムについての詳細はLをご覧ください。 =item (2) Cキーワードは多重継承をサポートしています(単にすべてのスーパークラスを配列の形でCに渡すだけです)。 extends 'Foo', 'Bar', 'Baz'; =item (3) Mooseはblessされたハッシュリファレンス以外のインスタンスもサポートしています(グロブリファレンスなど。Lをご覧ください)。 =back =head1 参照 =over 4 =item メソッドモディファイア メソッドモディファイアの概念はCLOSのものをそのまま借用しました。下記のリンク先にはすばらしい説明があります。 L =back =head1 作者 Stevan Little Estevan@iinteractive.comE Dave Rolsky Eautarch@urth.orgE =head1 COPYRIGHT AND LICENSE Copyright 2006-2009 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut