取势 明道 优术

作者为 扶 凯 发表

概要

  package Point;
  use Moose;

  has 'x' => (isa => 'Int', is => 'rw', required => 1);
  has 'y' => (isa => 'Int', is => 'rw', required => 1);

  sub clear {
      my $self = shift;
      $self->x(0);
      $self->y(0);
  }

  package Point3D;
  use Moose;

  extends 'Point';

  has 'z' => (isa => 'Int', is => 'rw', required => 1);

  after 'clear' => sub {
      my $self = shift;
      $self->z(0);
  };

  package main;

  # hash or hashrefs are ok for the constructor
  my $point1 = Point->new(x => 5, y => 7);
  my $point2 = Point->new({x => 5, y => 7});

  my $point3d = Point3D->new(x => 5, y => 42, z => -5);

 

描述

这是一个 Point 的类的例子,它来自 Perl 6 的 启示录 12 中的文档,类似于在经典的 K&R 风格的 C 语言书中的例子.
Moose 象普通的 Perl 5 的类一样,类是在包中定义的.Moose 会修改 strict 和 warings 的信息 ,记得在所有类文件中都需要包含 use Moose.如果没有这个, Perl 程序就会 die 掉.
当加载 Moose 时,他会导入和设置一些功能糖(sugar functions)到我们的 package 中.这意味着会导入一些 Moose 的关键字来实现一些功能.这些关键字并不是真实的 Perl 语言本身的关键字,这只是在使用 Moose 时导入扩展的 Perl 的功能进我们的包中.
Moose 会自动的建一些 Moose::Object 的子类在我们的包中.这些 Moose::Object 的类会提供一些功能来帮助构造类时候,可以对需要慎重对待的一些重要的属性进行操作.也还有一些其它的特性.深入了解可以看看 Moose::Object 的内容.

现在,看看这些关键字,这是我们学习的第一个 Moose 的关键字'has',我们看 has .他是用来定义类的属性.

has 'x' => (isa => 'Int', is => 'rw', required => 1); 

在这将会建一个名为 x 的属性.isa 是参数,他是用来设置这个值必须有的一些属性,在这是通过这个 Int 字符来设置成一个 Int 的类型约束.存取器(accessor) 会生成一个可以读写的属性.
注:Int 在是在 Moose 中提供的数字类型的约束,Int 是众多类型约束中的一种,更加的有关这些约束,可以看 Moose::Util::TypeConstraints.

这个地方的 required => 1 的参数,是指这个当新建对象时,这个 x 的属性必须提供.在 point 对象中如果没有坐标点,这个对象就没有多大意义了.所以我们不永许这样.
我们定义完我们的类的属性,接下来定义方法,在 Moose 中,这个规则很象 Perl 5 的 OO.从下面可以见到,这个包中的方法只是一个普通的子函数.

sub clear {
      my $self = shift;
      $self->x(0);
      $self->y(0);
}

好了,Point 的类,到这个地方就结束了.

接下有一个Point 的子类叫Point3D 的类,声明了他的 supperclass(超级类,父类),在这个地方声明所使用的也是 Moose 的关键字 'extends'.

extends 'Point';

这个 extends 的关键字很象 use base .首先,他会试图加载必须的一些类.然而,在 Moose 中不要使用 base ,这个 extends 关键字将会改写 @ISA 中提供的值.当使用 base 时会 push 一些值到你的包中的 @ISA 中.
另外,这是我认为 extends 的行为更为直观.
注:extends 支持多重继承,只要简单的这样写就可以 extends 'Foo', 'Bar', 'Baz';

 

接着为 Point3D 新建一个属性叫 z .

has 'z' => (isa => 'Int', is => 'rw', required => 1);

这个属性就象 Point 的 x 和 y 的属性.
在这的 after 关键字声明了 Moose 的一个新特性: 方法修饰(method modifiers)

after 'clear' => sub {
      my $self = shift;
      $self->z(0);
};

当从子类 Point3D 中调用 clear 时,当前这个定义的函数会运行,之所以称为关键字 after,是因为上面这个方法修饰,会在真正定义的函数后被调用

在这个例子中,这个真实的 clear 的方法是继承自 Point. 我们方法修饰的方法会接收相同的参数.
当然,使用 after 的方法修饰.并不是唯一的方法,因为这是 "perl",所以你能使用一些其它的方法得到相同的结果.如下

sub clear {
      my $self = shift;
      $self->SUPER::clear();
      $self->z(0);
}

你还可以使用作者 Moose 的方法修饰中的 'override';

override 'clear' => sub {
      my $self = shift;
      super();
      $self->z(0);
};

这个 override 的方法修饰可以让你使用 super 关键字来调度方法到 superclass 的方法中,非常象 Ruby-ish 风格.
至于是否要使用方法修饰,或是使用 Perl 本身的东西,这些考虑基本上只是一种你喜欢什么风格的问题.
自从 Point 继承了 Moose::Object 后,它也会继承一些默认的 Moose::Object 的构造函数.

my $point1 = Point->new(x => 5, y => 7);
my $point2 = Point->new({x => 5, y => 7});

my $point3d = Point3D->new(x => 5, y => 42, z => -5);

这新的构造函数接受前面定义过的属性的名字.你可以提供一个 hash 或者是 hash 的引用.因为这个例子有些特别,属性是必需的(前面使用了required => 1).不然调用 new 时,没这个属性,会提示出错.

my $point = Point->new( x => 5 ); # no y, kaboom!

到这后.我们能使用 $point 和 $point3d ,其它的使用,可以就象你平时使用 Perl 5 的对象一样.更多详细的例子,你可以看看 t/recipes/moose_cookbook_basics_recipe1.t 的测试文件.

 

Moose 对象只是一个 Hash 的引用

这些功能看起来相当的神奇,其中一个重要的认识就是 Moose 对象只是一个 hash 的引用,在这个例子中,你能通过 Data::Dumper 来看 $self ,可以详细的看到你想看到的.
你甚至可以直接操作对象的数据结构,但这是我们所强烈反对.
对 Moose 对象是一个 hash 的引用这个事实,就意味着非常容易的使用 Moose 来扩展非 Moose 的类.只要也是一个 hash 的引用.如果你想扩展非 hash 引用的类.你可以看看 MooseX::InsideOut.

 

 

 

来了就留个评论吧! 3个评论



    ladiu 2011年11月7日 的 07:49

    重要的认识。