取势 明道 优术

作者为 扶 凯 发表

概要

 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;
      }
  }

 

描述

这个指南演示的是使用 BUILDARGS 和 BUILD 的关键字.通过定义这些,方便我们在不重写 new 方法的提前下,就能 hook 进对象的构造过程中来.
在对象被创建前,这个 BUILDARGS 方法就会被调用和执行.它调用的是类方法.接收全部传给 new 的所有的参数.它通过这些参数做一些操作,并返回一个散列引用.散列的键的属性必须 init_args.
这 BUILDARGS 的主要目的是为了允许类接受命名参数以外的东西,在这个例子中,我们的 Person 的类,我们让他被调用使用单个参数(一个社保号).

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

下面 BUILDARGS 是个条件表达式:

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

 默认,Moose 构造器只接受键值对的列表或者 hash 的引用,所以在这我们在猜测它是一个社保号之前,必须确保这个 $_[0] 不是一个引用.

我们调用 original 的 BUILDARGS 的方法处理全部其它的参数,你必须让你的 BUILDARGS 的方法可以做这个,因为 Moose::Object 也提供了自己的 BUILDARGS 方法处理 hash 引用和键值对的列表
这个 BUILD 的方法工作在对象创建之后,才会被调用,然后在 return 返回之前调用. 所以这个 BUILD 的方法提供了一个机会用来检查对象的整体状态的一个很好的地方,但不能作为一个单一属性的类型约束的逻辑处理.所以在 BUILD 之类可以调用对象的所有方法.
在 Person 类中,需要检查country_of_residence 和 SSN 两个属性之间的关系.如果对象在逻辑不一致将会异常.

更加考虑

这指南简单得多,因为所有的属性为只读.如果 country_of_residence 属性设置了,如果国家是美国我们需要检查保证每个人有一个 SSN 号.

结论

我们曾多次 hook 来修改 Moose 的类,不用写一个新的类.这个指南演示了怎么样使用 BUILDARGS 和 BUILD 来 hook 到对象的构造的 new 方法中.
BUILDARGS 方法,让我们可以操作 Moose 的内置参数的构造函数处理.BUILD 的方法可以让我们在整个对象创建后进行逻辑上的约束.

 

来了就留个评论吧! 没有评论