題名¶
Moose::Cookbook::Roles::Recipe1 - Moose::Roleの例
概要¶
package Eq;
use Moose::Role;
requires 'equal_to';
sub not_equal_to {
my ( $self, $other ) = @_;
not $self->equal_to($other);
}
package Comparable;
use Moose::Role;
with 'Eq';
requires 'compare';
sub equal_to {
my ( $self, $other ) = @_;
$self->compare($other) == 0;
}
sub greater_than {
my ( $self, $other ) = @_;
$self->compare($other) == 1;
}
sub less_than {
my ( $self, $other ) = @_;
$self->compare($other) == -1;
}
sub greater_than_or_equal_to {
my ( $self, $other ) = @_;
$self->greater_than($other) || $self->equal_to($other);
}
sub less_than_or_equal_to {
my ( $self, $other ) = @_;
$self->less_than($other) || $self->equal_to($other);
}
package Printable;
use Moose::Role;
requires 'to_string';
package US::Currency;
use Moose;
with 'Comparable', 'Printable';
has 'amount' => ( is => 'rw', isa => 'Num', default => 0 );
sub compare {
my ( $self, $other ) = @_;
$self->amount <=> $other->amount;
}
sub to_string {
my $self = shift;
sprintf '$%0.2f USD' => $self->amount;
}
本文¶
Rolesにはおもに2つの目的があります。インタフェースと、コードの再利用です。このレシピでは、比較を定義するロールとオブジェクトのコードを表示するロールを利用して、コードの再利用について説明します。
まずはEq
から見ていきましょう。最初にuse Moose
がuse Moose::Role
になっているところに注目してください。また、requires
という新しいシュガー関数も使われています。
requires 'equal_to';
これは、このロールを取り込むクラスはかならずequal_to
というメソッドを用意する必要がある、という意味です。このメソッドは、ロールを取り込むクラスに直書きしてもよいですし、ほかのロールから取り込んでもかまいません。
Eq
ロールには、要求したequal_to
メソッドを利用したnot_equal_to
というメソッドが定義されています。このようにすると、ロールを取り込むクラスが用意しなければならないメソッドを最小限にとどめることができます。
次のComparable
ロールは、Eq
ロールを下敷きにして作られています。ここではwith
という新しいシュガー関数を利用してEq
をComparable
の中に取り込んでいます。
with 'Eq';
with
関数は取り込みたいロールのリストを引数に取ります。この例では、Eq
が要求しているequal_to
メソッドをComparable
ロールが用意していますが、そのようにしないこともできます(その場合はComparable
を取り込むクラスが自前でequal_to
を用意する必要があります)。言い換えると、ロールは必須メソッドを用意「しなくても」ほかのロールを取り込める、ということです。
Comparable
ロールはcompare
というメソッドを要求します。
requires 'compare';
また、Comparable
ロールはほかにも最終的にはcompare
に依存するいくつかのメソッドを提供しています。
sub equal_to {
my ( $self, $other ) = @_;
$self->compare($other) == 0;
}
sub greater_than {
my ( $self, $other ) = @_;
$self->compare($other) == 1;
}
sub less_than {
my ( $self, $other ) = @_;
$self->compare($other) == -1;
}
sub greater_than_or_equal_to {
my ( $self, $other ) = @_;
$self->greater_than($other) || $self->equal_to($other);
}
sub less_than_or_equal_to {
my ( $self, $other ) = @_;
$self->less_than($other) || $self->equal_to($other);
}
最後はPrintable
ロールです。このロールはインタフェースを提供するためだけに存在しています(メソッドはなく、必須メソッドのリストしかありません)。この場合、to_string
メソッドだけを要求しています。
インタフェースロールが便利なのは、メソッドと「名前」の双方を定義してくれるからです。このロールを組み込んだクラスはかならずto_string
メソッドを持っていることがわかるだけでなく、このメソッドが期待通りの意味を持っていることも想定できます(おそらく実際のコードではPrintable
ロールのドキュメントにメソッドの意味が書かれるはずです)。
最後のUS::Currency
クラスでは、Comparable
ロールとPrintable
ロールを両方取り込んでいます。
with 'Comparable', 'Printable';
また、amount
という、通常のMooseのアトリビュートも定義されています。
has 'amount' => ( is => 'rw', isa => 'Num', default => 0 );
最後に、私たちのロールが要求しているメソッドの実装を見ていきます。compare
メソッドはこうです。
sub compare {
my ( $self, $other ) = @_;
$self->amount <=> $other->amount;
}
Comparable
ロールを取り込んでこのメソッドを定義してやると、equal_to
、greater_than
、less_than
、greater_than_or_equal_to
、less_than_or_equal_to
というメソッドが自由に使えるようになります。
あとは、to_string
メソッドです。
sub to_string {
my $self = shift;
sprintf '$%0.2f USD' => $self->amount;
}
まとめ¶
ロールは非常に強力なものになりえます。再利用可能な振る舞いをカプセル化したり、クラスが提供するメソッドの(意味やインタフェースといった)情報をやりとりしたりするうえではすばらしいツールです。
1¶
-
Runner
とProcess
という2つのクラスがあるものと思ってください。どちらにもrun
というメソッドがありますが、オブジェクトにrun
メソッドの実装を要求するだけでは、このメソッドが「実際になにをするか」はまだなんとも言えません。ところが、Executable
ロールを実装しているオブジェクトを要求していれば、ある程度意味がわかるようになります。
作者¶
Stevan Little <[email protected]>
Dave Rolsky <[email protected]>
コピーライト & ライセンス¶
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.