取势 明道 优术

作者为 扶 凯 发表

 


NAME

Moose::Manual::Construction – 对象构造与销毁

 


VERSION

version 2.0401

 


什么事构造器

不要再去写狗日的new方法

当你已经开始使用moose时候,你的class就已经成为了the Moose::Object manpage的子类.the Moose::Object manpage 提供了一个new方法.如果你已经看过了the Moose::Manual::BestPractices manpage并且使得你的类immutable,那么你已经得到了一个内置的new方法,你可以像往常在perl5 oop中一样来访问它并且创建一个对象.

 


对象构造和属性

Moose提供的构造函数new方法接受哈希或者哈希引用类型的参数.你会发现用Moose来书写面向对象的代码非常容易,你再也不用去写那烦人的bless了.

 


对象构造中的HOOKS

Hooks,在英文里面是钩子的意思,就是再对象中嵌入一些增强功能的代码.例如你可以在对象构造的时候验证参数或者实现一些自己定制的需求.一般来说,我们推荐你使用BUILD来做这些事情,当然你也可以看看BUILDARGS.

当你布置好hooks之后,Moose会调度这些hooks来帮助你实现你想要的.关于hook的概念如果还有疑惑的话,可以看下面的代码:

    package Hook;
    sub new{
        my ( $class ) = ( shift,pop );
        my $self = {};
        bless $self,$class;
    }
    sub add_hooks{
        my $self = shift;
        my $hooks = shift;
        $self->{xx} = $hooks;
    }

    sub test{
        my $self = shift;
        # begin code
        # call hook ,do something you want to handle
        $self->{xx}->('xxx');

        #end code
    }
    1;

    Hook->add_hook( 'xxx',sub { print "hello" } );

总而言之就是在函数调用的时候加入一个hook,这个hook可能由于需求的变化你需要在这个函数里面定义一些你自己的代码,但是你觉得重载这个test方法又不是很好下手,因此只能在里面植入hook,相当于黏贴了一段代码到test函数里面.

 

BUILDARGS

BUILDARGS方法在对象构造之前被调用,它可以接受到所有传入对象的参数.也就是由new方法传入的参数,并且返回一个哈希引用.这个hashref最终用来构建对象.

一个最常见的使用BUILDARGS的例子就是使用它来实现非hashref的参数传递,因为默认moose的new是支持key-value参数传递,比如我们可能想Peson class可以通过社保卡号来直接访问对象, Person->new($ssn);

没有BUILDARGS方法处理的话,Moose处理的时候可能就不如你所愿了,因为默认的Moose是不能处理这样的参数的,我们使用BUILDARGS方法来实现这种不按key-value的参数构造.

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

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

这里面的$class->$orig.是调用的Moose内部的BUILDARGS,也就是超类中the Moose::Object manpage>内置的.我们通过BUILDARGS实现了参数传递的处理,在上面的代码里面,我们不难发现,实现上我们传递的还是key-value形式的参数.只是这中间多了一层处理,让你在外面构造对象的时候觉得就是通过值传递来构造的.

 

BUILD

BUILD方法在对象创建完之后被调用,有很多情况下,我们需要用到它.最普遍的情况就是我们要检测对象的状态.虽然Moose的Type系统可以检测绝大多数错误,但是还是有一些规则是需要我们自己来验证的.比如下面的代码

  sub BUILD {
      my $self = shift;

      if ( $self->country_of_residence eq 'USA' ) {
          die 'All US residents must have an SSN'
              unless $self->has_ssn;
      }
  }

另外一个使用BUILD方法的情况是用于追踪记录对象构造的过程.

  sub BUILD {
      my $self = shift;

      debug( 'Made a new person - SSN = ', $self->ssn, );
  }

当对象构造的时候,在日志中我们可以看到对象构造的记录.并且我们还可以在对象里面添加一些附加的信息.

  sub BUILD {
      my $self = shift;
      my $args = shift;

      $self->add_friend(
          My::User->new(
              user_id => $args->{user_id},
          )
      );
  }

 

BUILD and parent classes

BUILD方法在继承关系方面不像perl5 oop那样,你千万不要傻傻的写:

    $self->SUPER::BUILD

来调用Moose的BUILD方法,这并不可行,Moose会抛出一个异常.

Moose调用BUILD的方法的顺序是父类<Moose>->子类<你自己的类>,这似乎颠覆传统的对象调用方法顺序,不要感到惊讶,因为Moose就是这么干得.

当然这样的顺序是有理由的,因为BUILD方法只能应用于给对类构造的时候增加一些自定义的代码,也就是在对象构造完成之后,所以Moose总是先调用Moose内部的BUIlD方法,然后才是子类的.

 


对象销毁

Moose提供一个对象销毁的hook叫做DEMOLISH方法,和BUILD方法一样,你也不要用SUPER去调用,它的调用顺序也是先调用父类的方法.

多数情况下,你不会用到该方法,因为perl内置的垃圾回收机制都是有效的,不需要你再去提供一个<DEMOLISH>方法.

 

错误处理

当对象被销毁的时候,Moose在处理错误和异常的时候总是会local $?,如果你这时候callexit,exit代码将会被保存起来,即便对象销毁正在做system调用.

Moose同样会针对eval保存$@,如果你在DEMOLISH里面明确die,Moose会重复抛出错误.

如果你不喜欢这样的处理方式,你不得不去写自己的DESTROY方法取代Moose内置的.这里我就不再廒述perl5 oop里面的DESTROY了.

 


AUTHOR

斯文牛氓

如果你发现文章的bug和误区请发邮件至492003149@qq.com,我会尽量抽时间做出修改.

__END__

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