=encoding utf8 =head1 題名 Moose::Manual::Concepts - Mooseによるオブジェクト指向のコンセプト =head1 Mooseのコンセプト(と「従来の」Perl) 昔はパッケージとクラス、アトリビュートとメソッド、コンストラクタとメソッド等々の違いなんてあまり考えたことはなかったかもしれません。でも、Mooseの世界では、これらはすべて(たとえ裏では昔ながらのPerlで実装されているにしても)概念的には別々のものです。 私たちのメタオブジェクトプロトコル(MOP)はこれらの概念それぞれに対して明確に定義されたイントロスペクション機能を用意しています(Mooseはそのそれぞれに別のシュガー関数を用意しています)。また、Mooseはロールやメソッドモディファイア、宣言的な委譲といった概念も新たに導入しています。 Mooseの手ほどきとして、まずはこれらの概念がMoose語ではどういう意味になるのか、昔ながらのPerl 5のオブジェクト指向ではどのように使われてきたのかを調べてみましょう。 =head2 クラス パッケージの中で「use Moose」すると、そのパッケージはクラスになります。もっともシンプルなクラスにはアトリビュートやメソッドしかありませんが、ロールやメソッドモディファイアなどを含めることもできます。 クラスには0個以上のB<アトリビュート>があります。 クラスには0個以上のB<メソッド>があります。 クラスには0個以上のスーパークラス(親クラス)があります。クラスはスーパークラスを継承します。 クラスには0個以上のB<メソッドモディファイア>があります。このモディファイアは、そのクラスのメソッドだけでなく、祖先から継承したメソッドにも適用できます。 クラスには0個以上のB<ロール>があります(取り込めます)。 クラスにはB<コンストラクタ>とB<デストラクタ>があります。いずれもMooseが「ただで」提供してくれるものです。 B<コンストラクタ>はクラスのアトリビュートに対応した名前付きのパラメータを受け取り、それを使ってB<オブジェクトのインスタンス>を初期化します。 クラスにはB<メタクラス>があります。メタクラスにはB<メタアトリビュート>、B<メタメソッド>、B<メタロール>があります。メタクラスはそのクラスを説明するものです。 クラスはふつう「People」や「Users」のように複数の名詞が所属するカテゴリー名になぞらえられます。 package Person; use Moose; # now it's a Moose class! =head2 アトリビュート アトリビュートは、クラスを定義するプロパティです。アトリビュートには「かならず」名前があります。また、ほかのさまざまなプロパティを「持つこともあります」。 そのようなプロパティとしては、読み書きのフラグや、B<型>、アクセサメソッドの名前、B<委譲>、デフォルト値などがあります。 アトリビュートはメソッド「ではありません」が、アトリビュートを定義するとさまざまなアクセサメソッドが生成されます。ふつうのアトリビュートであれば最低でもかならず読み取り用のアクセサメソッドがありますし、多くのアトリビュートには書き込み用のメソッドやクリア用のメソッド、(「値がセットされていますか」という)断定用のメソッドなどもあるものです。 アトリビュートにはB<委譲>が定義されていることもあります。これは委譲のマッピングに基づいて追加のメソッドを生成するものです。 デフォルトでは、Mooseはアトリビュートをオブジェクトのインスタンス(これはハッシュリファレンスです)の中に保存しますが、「これはMooseベースのクラスの作者には見えないことになっています」! Mooseのアトリビュートは「不透明な」B<オブジェクトのインスタンス>の「プロパティ」であると考えておくのがいちばんです。これらのプロパティには、明確に定義されたアクセサメソッドを通じてアクセスできます。 アトリビュートはそのクラスに所属しているメンバなら持っている、というものです(たとえば、Peopleクラスのメンバなら姓と名を持っていますし、Usersクラスのメンバであればパスワードと最終ログイン日時を持っています)。 has 'first_name' => ( is => 'rw', isa => 'Str', ); =head2 メソッド B<メソッド>は非常に単純。クラスの中で定義したサブルーチンはすべてメソッドです。 B<メソッド>は動詞に対応するもので、オブジェクトができることをあらわします(たとえば、Userならログインできます)。 sub login { ... } =head2 ロール ロールは、クラスが「果たす」役割をあらわすものです。また、クラスがロールを「消費する」という言い方もします。たとえば、MachineクラスならBreakableという役割を果たすかもしれません。Boneクラスもそうかもしれません。ロールは複数の無関係なクラスが共通して持つ(「壊せる」とか「色がついている」という)概念を定義するのに使います。 ロールには0個以上のB<アトリビュート>があります。 ロールには0個以上のB<メソッド>があります。 ロールには0個以上のB<メソッドモディファイア>があります。 ロールには0個以上のB<必須メソッド>があります。 必須メソッドはそのロールが実装するものではありません。必須メソッドは「このロールを使うにはかならずこのメソッドを実装する必要がある」という意味です。 ロールには0個以上のB<排除ロール>があります。 排除ロールというのは、その排除しようとしているロールとは合成できないロールのことです。 ロールはクラス(や、ほかのロール)に合成されるものです。ロールをクラスに合成すると、そのアトリビュートやメソッドはそのクラスに「埋め込まれます(フラット化)」。ロールが継承階層にあらわれることは「ありません」。ロールを合成すると、そのアトリビュートやメソッドは「その取り込んだクラスの中で」定義されていたかのように見えます。 ロールは、ほかのオブジェクト指向言語でいうミックスインやインタフェースに似たものです。 package Breakable; use Moose::Role; has is_broken => ( is => 'rw', isa => 'Bool', ); requires 'break'; before 'break' => { my $self = shift; $self->is_broken(1); }; =head2 メソッドモディファイア B<メソッドモディファイア>は指定したメソッドが呼ばれたときに呼ばれる割り込みのことです。たとえば、「Cを呼ぶ前にこのモディファイアを先に呼んでほしい」のように言えるわけです。モディファイアには「before」、「after」、「around」、「augment」のようにさまざまな種類のものがあります。また、ひとつのメソッドに複数のモディファイアを適用することもできます。 メソッドモディファイアはよく親クラスのメソッドをオーバーライドするかわりに使われます。また、ロールの中でも、ロールを取り込む側のクラスのメソッドを修正する手段として利用されます。 メソッドモディファイアは、裏では単に指定したメソッドが呼ばれる前やあとに(あるいはそれを取り囲むように)呼ばれる昔ながらのPerlのサブルーチンにすぎません。 before 'login' => sub { my $self = shift; my $pw = shift; warn "Called login() with $pw\n"; }; =head2 型 Mooseには(ミニチュアの)型システムもあります。この型システムを使うと、アトリビュートに型を定義できるようになります。Mooseには、CやC、C、Cのように、Perlに用意されている型に基づいた組み込みの型がひと揃い用意されています。 また、アプリケーションで使われているクラスの名前はすべて、型の名前として使うことができます。 最後に、サブタイプとして、あるいは完全に新しい型として、独自の制約を持つ自前の型を定義することもできます。たとえば、Cのサブタイプとして、正数しか許さないCという型を定義できます。 =head2 委譲 Mooseのアトリビュートには委譲を宣言的に定義する構文が用意されています。委譲というのは、実際の仕事はアトリビュートのメソッドを呼び出して行わせるメソッドのことです。 =head2 コンストラクタ コンストラクタはB<オブジェクトのインスタンス>を生成するものです。従来のPerlではCというメソッドを定義して、そこでリファレンスをCするのがふつうでした。 MooseはこのCというメソッドを生成して適切な処理をしてくれます。自分でコンストラクタを定義する必要はまったくありません! 場合によってはオブジェクトを生成するときに何かしたいことがあります。そのようなときはクラスにCメソッドを用意すると、Mooseが新しいオブジェクトを生成したあとでそのメソッドを呼んでくれます。 =head2 デストラクタ これはオブジェクトのインスタンスがスコープの外に出たときに呼ばれる特殊なメソッドです。必要があれば、このメソッドの中でクラスに特殊な処理をさせられます(ただし、ふつうはそのようなことをする必要はありません)。 これは、従来のPerl 5ではCメソッドですが、MooseではCメソッドになります。 =head2 オブジェクトのインスタンス オブジェクトのインスタンスは、たとえば具体的なひとりのPersonやUserのように、そのクラスの「カテゴリー」に属する具体的な名詞にあたります。インスタンスはクラスのB<コンストラクタ>によって生成されます。 インスタンスはアトリビュートの値を持っています(たとえば、具体的な人物には(具体的な)姓と名があります)。 インスタンスは、従来のPerl 5ではblessされたハッシュリファレンスであることが多いですが、Mooseの場合、オブジェクトのインスタンスが実際にどうなっているかを知る必要はまったくありません(はいはい。Mooseの場合でもふつうはblessされたハッシュリファレンスです)。 =head2 Mooseと従来のPerlの違いのまとめ =over 4 =item * クラス シンボルテーブルの中を漁る以外にはイントロスペクションがないパッケージ。 Mooseを使うと、明確に定義された宣言とイントロスペクションを利用できます。 =item * アトリビュート 手書きのアクセサメソッド。シンボルテーブルのハック。あるいはCのようなヘルパーモジュール。 Mooseでは宣言的に定義され、メソッドとは明確に区別されています。 =item * メソッド これはMooseでも従来のPerlとだいたい同じです。 =item * ロール CやC、あるいはCかも。 Mooseではコア機能に含まれています。ほかのすべてのものと同様にイントロスペクション可能です。 =item * メソッドモディファイア 昔はシンボルテーブルを本気でいじらないとできませんでした。(少なくともPerl 5では)おそらくこれまでに見たことのある人はいなかったはずです。 =item * 型 Cメソッドやアクセサの中で手書きのパラメータチェック。 Mooseでは宣言的に型を定義しておいて、アトリビュートの中でその型を名指しで利用するようになっています。 =item * 委譲 C or C, but probably even more hand-written code. Mooseではこれも宣言的にできます。 =item * コンストラクタ Cメソッド。その中でリファレンスをCします。 Mooseでクラスを定義すればただでついてきます。 =item * デストラクタ Cメソッド。 MooseではCと呼ばれます。 =item * オブジェクトのインスタンス blessされたリファレンス(ふつうはハッシュリファレンスです)。 Mooseでは中身は見えませんが、クラスによって定義されたアトリビュートやメソッドがたくさんあります。 =item * 不変化 Mooseには「不変化」と呼ばれる機能があります。クラスを不変化すると、メソッドやアトリビュート、ロールなどの追加は済んだとみなされ、Mooseがその場限りの非常に汚いコードを生成するテクニックを駆使してクラスを最適化し、オブジェクトの生成などを高速化します。 =back =head1 メタって何なの メタクラスはクラスを説明するクラスです。Mooseを使って定義したクラスにはすべてCメソッドが用意されるのですが、このメソッドが返すLオブジェクトのイントロスペクションAPIを使うと、そのクラスについての情報を調べることができます。 my $meta = User->meta(); for my $attribute ( $meta->get_all_attributes ) { print $attribute->name(), "\n"; if ( $attribute->has_type_constraint ) { print " type: ", $attribute->type_constraint->name, "\n"; } } for my $method ( $meta->get_all_methods ) { print $method->name, "\n"; } メタクラスは、先ほど定義した概念のほとんどすべてにあります(だから、LやL、L、L、L、Lなどがあるわけです)。 =head1 どうしても自分のやり方でしたいときは Mooseのすごいところのひとつは、深入りしていって何か「間違ったやり方」をしているものを見つけたとしても、メタクラスを拡張すれば変えられることです。たとえば、配列リファレンスをベースにしたオブジェクトを持てるようにもできますし、コンストラクタを厳密に(知らないパラメータは許さないように!)することもできます。アトリビュートのアクセサの命名規則を定義することもできますし、クラスをシングルトンにすることも、それ以上のことだってできるのです。 こういった拡張モジュールの多くは、驚くほど少ないコードしか要求しませんし、一度作ってしまえば、「自分ならこうする」というコードをまた手書きする必要はなくなります(そのかわりに自分の好きな拡張モジュールをロードしてしまえばよいのです)。 package MyWay::User; use Moose; use MooseX::StrictConstructor use MooseX::MyWay; has ...; =head1 次にするべきことは Mooseの売り文句に納得していただけたら、今度は本当に使い方を学ぶ番です。 Mooseをそのまま従来のPerl 5のオブジェクト指向のコードで書き直したらどうなるかを見たい方は、Lをご覧ください。「Mooseの流儀」の一端を手っ取り早く理解するには便利かもしれません。 あるいは、それは飛ばして、まっすぐLや、残りのLに行ってもよいでしょう。 そのあとは、Lから始めることをおすすめします。基本のセクションのレシピをすべて読み終えたら、Mooseの動作や、基本的なオブジェクト指向の機能についてはすべて、かなりよく理解できているはずです。 そのあとは、ロールのレシピをご覧ください。本気で知りたいのであれば、そのままメタや拡張モジュールについてのレシピも読んでいきましょう。ただし、これらはおもにMooseの達人になりたい人や、Mooseの挙動を変えたい人向けのものです。 =head1 作者 Dave Rolsky Eautarch@urth.orgE =head1 COPYRIGHT AND LICENSE Copyright 2008-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