跳转到内容

使用 Moose 编程/解决的问题/访问器

来自维基教科书,开放世界中的开放书籍

Perl 有两种聚合(非标量)数据类型:数组和哈希(即关联数组),它们通常用作对象的基数据结构。一个更严重的问题是这些数据类型都不是对象。例如,本机数组和哈希支持不允许您在向哈希写入新的键值对时或将新元素推入数组时接收事件触发器。可以使用 Perl 的 tie() 机制来实现这一点,但 tie() 会带来严重的性能损失。Perl 确实增强了 C 样式的固定大小数组 - 一个简单的连续内存块,通过索引寻址 - 以便它可以根据需要增长或收缩,但出于效率原因选择了仅实现基本操作。[1]

两者中的一种可能更适合一项任务,而不适用于另一项任务,但这并不重要。使用 Moose,您的程序不会直接绑定到 Perl 数据结构。

旧方法

[编辑 | 编辑源代码]

传统上,Class::Accessor 在这个领域做了最多的事情,我们很快就会看到这一点。它解决的问题很大也很简单,而且它没有过度实现:如果你有一个哈希,你不想插入一个 key: foobar,你该怎么办?你只需将哈希 bless 到一个对象中,并说明应该生成哪些访问器。现在这个“哈希”充当你的模块可以包装的简化模型,以实现程序的目标。当调用一个不相关的函数时,perl 会意识到它无法将方法解析到匹配的子程序,并报错。[2]

一个例子

[编辑 | 编辑源代码]

仔细看看这段代码的简洁性。

package OldWay;
use strict;
use warnings;

use base 'Class::Accessor'; ## or C::A::Fast, or C::A::More, et al.

BEGIN { Class::Accessor->mk_accessors( qw/ foo bar baz / ) }

sub new {
	my $class = shift;
	bless {}, $class;
}

package main;
use strict;
use warnings;

my $obj = OldWay->new;

## These work
$obj->foo( 1 );
$obj->bar( OldWay->new );
$obj->baz( 'foo' );

## Anything else will die
$obj->quz( 1 ); #die

在这里,我们创建了一个对象,本质上是一个只允许三个键的哈希。这个哈希的 getter 和 setter 与底层 perl 哈希中的键直接相关。请记住,Perl 对象非常简单。复杂性留给程序员作为练习。

调用 Moose

[编辑 | 编辑源代码]

说实话,Moose 不仅仅是 Class::Accessor 的替代品。因此,它不会与 Class::Accessor 的简洁性竞争。然而,在本节中,我们将看到 Moose 的类似物。[3]

一个例子

[编辑 | 编辑源代码]
package NewWay;
use Moose;

has 'foo' => ( isa  => 'Value', is => 'rw' );
has 'bar' => ( isa  => 'Value', is => 'rw' );
has 'baz' => ( isa  => 'Value', is => 'rw' );
# or just
# has [qw/ foo bar baz /] => ( isa  => 'Value', is => 'rw' );

package main;
use strict;
use warnings;

my $obj = NewWay->new;

## Rest of the module is the same..

## These work
$obj->foo( 1 );
$obj->bar( NewWay->new );
$obj->baz( 'foo' );

## Anything else will die
$obj->quz( 1 ); #die

非常简单。到目前为止,没有任何语法真正需要解释——但在下一节中,我们将解释。

新语法

[编辑 | 编辑源代码]

为了清晰起见,以及那些无法通过示例学习的人

has 是少数几个 Moose 关键字之一,它开始了一个声明语句的 Moose 表达式。

另请参阅:has

has 的上下文中,isa 指的是对象的 TypeConstraint。Moose 具有内置的类型系统,这个关键字(将在下一章中进一步解释)指定了该属性将接受哪些值。

另请参阅:has

has 的上下文中,is 指的是要创建的访问器类型。值为 rw 将指示 Moose 生成 setter 和 getter,而值为 ro 将只生成 getter。

  1. ^ 除非你显式地制作了一个 sub AUTOLOAD {},否则我们不要理会这个例外。
  2. ^ 固定是指你可以push到数组的两端。不能保证新数据与旧数据是连续的。当你用 perl 编程时,你不应该在低级思考,不用担心会发生什么,只要知道它会起作用。
  3. ^ 当然,使用 Moose::Tiny,你可能会得到一个比 Class::Accessor 更强大、更简洁的替代方案,但我们将在本书的后面部分讨论 MooseX:: 和替代方案。
华夏公益教科书