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