跳转到内容

使用 Moose 编程/难点/Moose 与 Moose

来自维基教科书,开放书籍,构建开放世界

在使用 Moose 一段时间后,你最终会发现自己拥有了一系列想要重复使用的类。不幸的是,Moose 不会让在 Moose 内部使用这些 Moose 类变得容易。以下是在 Moose 内部执行的一些困难任务[1]

在这些示例中,MooseObj 是一个 Moose 对象。

  • isa => ArrayRef[ MooseObj ]
    • 没有语法糖来方便创建 MooseObj。
    • 无法从 $self->MooseObj( [{constructor_args1}, {constructor_args2}] ) 轻松强制转换。
  • isa => 'MooseObj'
    • 没有对 MooseObj 的语法自动装箱,你不能执行以下操作:$self->foo->new( {constructor_args} )
    • 没有隐式方法绕过 MooseObj 的创建并设置其访问器。更多信息请参见 一对一自动构建

一对一自动构建

[编辑 | 编辑源代码]

如果你不关心 MooseObj,只关心内部的访问器怎么办?子对象可能只是作为更多访问器的逻辑分组,如以下示例所示。

package Address;
use Moose;

has 'street' => ( isa => 'Str', required => 1, is => 'ro' );
has 'city'   => ( isa => 'Str', required => 1, is => 'ro' );
has 'zip'    => ( isa => 'Str', required => 1, is => 'ro' );
has 'state'  => ( isa => 'Str', required => 1, is => 'ro' );
package USProfile;
use Moose;

has 'workAddress' => ( isa => 'Address', is => 'ro' );
has 'homeAddress' => ( isa => 'Address', is => 'ro' );

即使 Moose 非常努力地替换和抽象掉许多 Perl 的基本类型,这里与使用 Address 哈希相比,它使工作变得困难。以下是一些明显的困难。

  • 你不能跳过创建新Address对象
    • 语法 $USProfile->workAddress->street('1234 FooStreet') 无效。
    • 语法 $USProfile->workAddress->{street} = '1324 FooStreet' 将是有效的(假设workAddress是哈希)。
  • 你可能希望组件类Address要求其子属性,但没有办法在该类用作另一个类的子类时,禁用这些 requires=>1
    • 语法 has 'workAddress' => ( isa => 'Address', is => 'ro', default => sub{Address->new} ); 无效。
    • 由于与 is=>'ro' 类似的问题,这在这个情况下并不十分有用。

建议解决方案

[编辑 | 编辑源代码]

本节正在重写 现在显然应该发生一些漂亮的事情来方便创建电子邮件。在这里进行的任何工作都会极大地促进 Moose 在基于令牌的系统中的应用,例如 XML,你可以在 Moose 中轻松地建模一个一级树,然后将其拉入另一个 Moose 类以创建多级文档。以下是尚未实现的伪代码。

my $person = USProfile->new;
## Hack a handles new into the Email code, which calls its own constructor
my $email = $person->email->new({  (constructor)  })

## Still permit some sort of construction that suffices the required
## obviously a no-go if you have it embedded in an ArrayRef or HashRef
my $person => USProfile->new({state=>'TX', email=>{[moose_args=>]constructor args} }

可能需要为继承自 Moose::Object 的事物设置一组 AttributeHelpers。

另一种实现此目的的方法类似于(由 steven 提供)。

has hash => (
  is => "ro"
  , default => sub { blessed(shift)->new }
  , lazy => 1
);

my $h = Hash->new;
$h->hash->hash->hash;
 print $h->dump;'

这里有一些问题。

  • blessed(shift)->new 应该是 isa=> 的值。
  • 它不适用于 isa,这些 isarequired=>1,必须有一些方法自动构建,或者发送构造函数参数。
  • ^ 困难或过于冗长
华夏公益教科书