写入Perl Moose类中的只读属性

tjw*_*992 6 perl accessor moose

使用Perl和Moose,可以通过两种方式访问​​对象数据.

$self->{attribute} 要么 $self->attribute()

这是一个简单的例子,展示了两者:

# Person.pm
package Person;

use strict;
use warnings;
use Moose;

has 'name' => (is => 'rw', isa => 'Str');
has 'age'  => (is => 'ro', isa => 'Int');

sub HAPPY_BIRTHDAY {
    my $self = shift;
    $self->{age}++;   # Age is accessed through method 1
}

sub HAPPY_BIRTHDAY2 {
    my $self = shift;
    my $age = $self->age();
    $self->age($age + 1);   # Age is accessed through method 2 (this will fail)
}

1;
Run Code Online (Sandbox Code Playgroud)
# test.pl
#!/usr/bin/perl

use strict;
use warnings;
use Person;

my $person = Person->new(
    name => 'Joe',
    age  => 23,
);

print $person->age()."\n";

$person->HAPPY_BIRTHDAY();
print $person->age()."\n";

$person->HAPPY_BIRTHDAY2();
print $person->age()."\n";
Run Code Online (Sandbox Code Playgroud)

我知道当你在Person.pm文件之外时最好使用该$person->age()版本,因为它可以防止你犯下愚蠢的错误并阻止你覆盖只读值,但我的问题是......

里面Person.pm是它最好使用$self->{age}$self->age()?覆盖模块本身的只读属性被认为是不好的做法吗?

如果该属性预期会发生变化,是否应将此属性更改为读/写属性,或者通过$self->{age}HAPPY_BIRTHDAY函数内使用来覆盖属性的只读方面是否可接受?

fri*_*edo 7

使用Moose时,最佳做法是始终使用生成的访问器方法,即使在对象自己的类中也是如此.原因如下:

  1. 访问器方法可能被执行特殊操作的子类覆盖.调用$self->age()确保将调用正确的方法.

  2. 可能存在附加到属性的方法修饰符,例如beforeafter.直接访问哈希值将跳过这些.

  3. 可能存在附加到属性的谓词或更清晰的方法(例如has_age).直接使用哈希值进行混乱会使它们混淆.

  4. 哈希键受拼写错误.如果你不小心说$self->{aeg}错误将不会立即被抓住.但是$self->aeg因为该方法不存在会死亡.

  5. 一致性很好.没有理由在一个地方使用一种风格而在其他地方使用另一种风格.它使代码更易于理解.

在只读属性的特定情况下,以下是一些策略:

  1. 使您的对象真正不可变.如果需要更改值,请构造一个新对象,该对象是具有新值的旧对象的克隆.

  2. 使用只读属性存储实际年龄,并指定私有编写器方法

例如:

package Person;
use Moose;

has age => ( is => 'ro', isa => 'Int', writer => '_set_age' );

sub HAPPY_BIRTHDAY {
    my $self = shift;
    $self->_set_age( $self->age + 1 );
}
Run Code Online (Sandbox Code Playgroud)

更新

这是一个如何使用延迟构建器基于另一个设置一个属性的示例.

package Person;
use Moose;

has age     => ( is => 'rw', isa => 'Int', lazy => 1, builder => '_build_age' );
has is_baby => ( is => 'rw', isa => 'Bool', required => 1 );

sub _build_age { 
    my $self = shift;
    return $self->is_baby ? 1 : 52
}
Run Code Online (Sandbox Code Playgroud)

懒惰的构建器在age被访问之前不会被调用,因此您可以确定is_baby它将存在.

直接设置哈希元素当然会跳过构建器方法.

  • 你的`_real_age`建议是个坏主意.相反,使用具有私有编写器方法的只读属性:`is =>'ro',writer =>'_ set_age'.这样,您不会因为读取属性而产生两个方法调用的开销. (2认同)
  • 您还可以将[特征](https://metacpan.org/pod/Moose::Meta::Attribute::Native)引入您的年龄属性.这将为您提供_birthday_方法,您仍然可以确保年龄只会上升.毕竟我们不会变得年轻.让我们使用[反特质](https://metacpan.org/pod/Moose::Meta::Attribute::Native::Trait::Counter).`has age =>(is =>'ro',traits => ['Counter'],isa =>'Num',handles => {celeb_birthday =>'inc'},);`现在我们有一个读 - 只有年龄必须是一个数字.试图设置它会爆炸,但你可以通过调用`$ bob-> celeb_birthday`来增加. (2认同)