Moose > Moose::Cookbook::Basics::Recipe10

題名

Moose::Cookbook::Basics::Recipe10 - BUILDARGSとBUILDを使ってオブジェクトの生成に割り込む

概要

  package Person;

  has 'ssn' => (
      is        => 'ro',
      isa       => 'Str',
      predicate => 'has_ssn',
  );

  has 'country_of_residence' => (
      is      => 'ro',
      isa     => 'Str',
      default => 'usa'
  );

  has 'first_name' => (
      is  => 'ro',
      isa => 'Str',
  );

  has 'last_name' => (
      is  => 'ro',
      isa => 'Str',
  );

  around BUILDARGS => sub {
      my $orig = shift;
      my $class = shift;

      if ( @_ == 1 && ! ref $_[0] ) {
          return $class->orig( ssn => $_[0] );
      }
      else {
          return $class->$orig(@_);
      }
  }

  sub BUILD {
      my $self = shift;

      if ( $self->country_of_residence eq 'usa' ) {
          die 'Cannot create a Person who lives in the USA without an ssn.'
              unless $self->has_ssn;
      }
  }

本文

このレシピではBUILDARGSBUILDの使い方を説明します。これらのメソッドを定義すると、newをオーバーライドしなくてもオブジェクトの生成プロセスに割り込むことができます。

BUILDARGSメソッドが呼ばれるのはオブジェクトが生成される「前」です。これはクラスメソッドとして呼ばれ、newメソッドに渡されるすべてのパラメータを受け取ります。BUILDARGSメソッドはその引数に何らかの処理をして、ハッシュリファレンスを返すことが期待されています。ハッシュの各キーはアトリビュートのinit_argでなければなりません。

BUILDARGSの主な目的は、クラスが名前付き引数以外のものを受け取れるようにすることです。このPersonクラスの場合は引数が社会保障番号ひとつだけでも呼べるようにしています。

  my $person = Person->new('123-45-6789');

今回のBUILDARGSのポイントはこの条件文です。

      if ( @_ == 1 && ! ref $_[0] ) {
          $class->$orig( ssn => $_[0] );
      }

Mooseのコンストラクタは、デフォルトではキーと値のペアからなるリストか、ハッシュリファレンスを受け取るようになっているので、リファレンスでないことを確認してからでないと$_[0]が社会保障番号であると想定することはできません。

親クラスのBUILDARGSを呼んでいるのは、それ以外のすべてのケースに対応するためです。自分でBUILDARGSメソッドを実装する場合はかならずこのようにしておいてください。Moose::Objectにもハッシュリファレンスやキーと値のペアのリストを処理する独自のBUILDARGSメソッドが用意されているためです。

BUILDメソッドが呼ばれるのはオブジェクトが生成された「あと」、呼び出し元にオブジェクトを返す前です。BUILDメソッドを使うと全体的なオブジェクトの状態をチェックできます。BUILDメソッドは個々のアトリビュートの型制約だけでは表現できないロジックを入れておくのに便利です。

Personクラスの場合はssncountry_of_residenceという2つのアトリビュートの関係をチェックする必要があります。オブジェクトが論理的に首尾一貫していない場合は例外が発生します。

検討課題

このレシピではすべてのアトリビュートが読み取り専用なのでかなり単純になっていますが、country_of_residenceアトリビュートが設定可能だったら、新しい居住国がusaだったときにssnが設定されているかチェックする必要が出てきます(このようなチェックはbeforeモディファイアを使えば実現できるかもしれません)。

まとめ

これまで何度もMooseのクラスではnewをオーバーライドしないようにと言ってきました。このレシピでは、newをオーバーライドしなくてもBUILDARGSBUILDを使えばオブジェクトの生成に割り込めることを紹介しました。

BUILDARGSメソッドを使うと、Mooseに組み込まれているコンストラクタのパラメータ処理を拡張できます。BUILDメソッドを使うと、オブジェクト生成後にオブジェクト全体にかかわる論理的な制約を実装できます。

作者

Dave Rolsky <autarch@urth.org>

コピーライト & ライセンス

Copyright 2006-2009 by Infinity Interactive, Inc.

http://www.iinteractive.com

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.