初めてのMoose - Mooseのすすめ

シンプルなPerl 5でクラスを作る

普通のPerl5 OOでクラスを書くと

{

    package Dog;

    use strict;
    use warnings;

    sub new {
        my ( $class, %args ) = @_;
        my $obj = bless {
            name   => $args{name},
            gender => $args{gender},
        }, $class;

        return $obj;
    }

    sub name {
        my $self = shift;
        $self->{name} = shift if @_;
        return $self->{name};
    }

    sub gender {
        my $self = shift;
        $self->{gender} = shift if @_;
        return $self->{gender};
    }

    sub bark {
        my $self = shift;

        print "Bow Wow!\n";
    }
}

use strict;
use warnings;

my $dog = Dog->new( gender => 'male' );
$dog->name('Pochi');
print 'gender: ' . $dog->gender . "\n";
print 'name: ' . $dog->name . "\n";
$dog->bark;

良くありがちなDog(犬)クラスですね。
名前(name)と性別(gender)というクラス属性(アトリビュート)へのアクセサを持っていて、吼える(bark)っていうメソッドを持っています。
コンストラクタ引数にはnameとgenderを指定できます。

で、実行すると

gender: male
name: Pochi
Bow Wow!

と表示されます。

Perl 5のOOは意外とシンプルなのですが、ちょっと冗長だし新しいアトリビュートが追加されるたびにアクセサを作るのは面倒です。

Class::Accessorを使ってクラスを作る

で、CPANを漁ってみるとClass::Accessorという便利なモジュールがあるので書き直してみます。

{

    package Dog;

    use strict;
    use warnings;

    use base qw(Class::Accessor);

    __PACKAGE__->mk_accessors(qw(name gender age));

    sub bark {
        my $self = shift;

        print "Bow Wow!\n";
    }
}

use strict;
use warnings;

my $dog = Dog->new( gender => 'male' );
$dog->name('Pochi');
$dog->age('three');
print 'gender: ' . $dog->gender . "\n";
print 'name: ' . $dog->name . "\n";
print 'age: ' . $dog->age . "\n";
$dog->bark;

ずいぶんすっきりした上に年齢(age)へのアクセサも追加できました。
Class::Accessorでは新しいアクセサを追加したくなったらmk_accessorで追加するだけです。
コンストラクタの引数がハッシュからハッシュリファレンスに変わったことを除けば、さきほどのに加えて年齢も表示されるようになりました。

Class::Accessorは色々なモジュールで使われており、ちょっとしたクラスを作る時には非常に便利です。

が、このクラスの要件に新しく「ageアクセサは整数のみ受けつける」と言うものが追加されたらどうでしょう?
こうなるとClass::Accessorの恩恵を受けることはできなくなり、コンストラクタとageメソッドを自分で実装する必要がでてきます。

Mooseを使ってクラスを作る

そこで、Mooseを使ってこの要件を含めて実装してみます。

{

    package Dog;

    use Moose;

    has name   => ( is => 'rw', isa => 'Str' );
    has gender => ( is => 'rw', isa => 'Str' );
    has age    => ( is => 'rw', isa => 'Int' );

    sub bark {
        my $self = shift;

        print "Bow Wow!\n";
    }
}

use strict;
use warnings;

my $dog = Dog->new( gender => 'male' );
$dog->name('Pochi');
$dog->age('three');
print 'gender: ' . $dog->gender . "\n";
print 'name: ' . $dog->name . "\n";
print 'age: ' . $dog->age . "\n";
$dog->bark;

これを実行すると

Attribute (age) does not pass the type constraint because: Validation failed for
'Int' failed with value three at (eval 72) line 7
        Dog::age('Dog=HASH(0x9df9fe8)', 'three') called at moose.pl line 23

と、エラーが表示され実行できないことがわかります。
これはageアトリビュート(has age)は読み書き可能(is => 'rw')だけど、整数のみ値として保持できる(is => 'Int')ように設定されているからです。

$dog->age(3);

のように直すと、先ほどと同じ結果を得ることができます。

このようにMooseは単なるPerl 5 OOの焼き直しだけではなく、より簡単により強力なOOプログラミングを提供してくれるモジュールです。

先ほどの型制約だけではなく、型強制変換(coerce)、インターフェース(role)、メタクラスへのアクセスなどの機能も提供してくれます。