写入 Perl Moose 中的只读属性 class
Writing to read-only attributes inside a Perl Moose class
使用 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;
# 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";
我知道当你在 Person.pm
文件之外时,最好使用 $person->age()
版本,因为它可以防止你犯愚蠢的错误并阻止你覆盖只读值, 但我的问题是...
Inside of Person.pm
is it best to use $self->{age}
or $self->age()
? Is it considered bad practice to overwrite a read-only attribute within the module itself?
Should this attribute be changed to a read/write attribute if its value is ever expected to change, or is it considered acceptable to override the read-only aspect of the attribute by using $self->{age}
within the HAPPY_BIRTHDAY
function?
我不认为 $self->{age}
是一个文档化的接口,所以它甚至不能保证工作。
在这种情况下,我会使用 https://metacpan.org/pod/Moose::Manual::Attributes#Accessor-methods 中所述的私人编写器:
has 'weight' => (
is => 'ro',
writer => '_set_weight',
);
您甚至可以使用 https://metacpan.org/pod/MooseX::AttributeShortcuts#is-rwp 中的 'rwp'
自动执行此操作:
use MooseX::AttributeShortcuts;
has 'weight' => (
is => 'rwp',
);
使用 Moose 时,最佳做法是始终使用生成的访问器方法,即使在对象自己的方法中也是如此 class。以下是几个原因:
访问器方法可能会被执行特殊操作的子 class 覆盖。调用 $self->age()
确保调用正确的方法。
可能有方法修饰符,例如 before
或 after
,附加到属性。直接访问哈希值将跳过这些。
可能有一个谓词或更清晰的方法附加到属性(例如 has_age
)。直接乱用哈希值会让他们迷惑。
散列键容易出现拼写错误。如果你不小心说了 $self->{aeg}
错误将不会被立即捕获。但是 $self->aeg
会死掉,因为这个方法不存在。
一致性好。没有理由在一个地方使用一种风格而在其他地方使用另一种风格。它也使新手更容易理解代码。
在只读属性的特定情况下,这里有一些策略:
使您的对象真正不可变。如果您需要更改一个值,请构造一个新对象,该对象是具有新值的旧对象的克隆。
使用只读属性存储真实年龄,指定私有写法
例如:
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 );
}
更新
下面是一个示例,说明您可以如何使用惰性构建器根据一个属性设置另一个属性。
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
}
直到访问 age
才会调用惰性构建器,因此您可以确定 is_baby
会在那里。
直接设置hash元素当然会跳过builder方法
开箱即用的 perl 不是类型安全的,也没有太多封装方式,因此很容易做出鲁莽的事情。 Moose 在您的 perl 对象上强加了一些文明,用安全和稳定换取了一些自由。如果 Moose 变得太令人窒息,底层的 Perl 仍然存在,所以有办法绕过 Moose 的铁拳试图制定的任何法律。
一旦您意识到您已将属性声明为只读,但您想要更改它,即使您也说过您希望它是只读的,并且在大多数宇宙中您将某些内容声明为只读,因为您不想更改它,然后一定要继续更新 $person->{age}
。毕竟,你知道自己在做什么。
使用 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;
# 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";
我知道当你在 Person.pm
文件之外时,最好使用 $person->age()
版本,因为它可以防止你犯愚蠢的错误并阻止你覆盖只读值, 但我的问题是...
Inside of
Person.pm
is it best to use$self->{age}
or$self->age()
? Is it considered bad practice to overwrite a read-only attribute within the module itself?Should this attribute be changed to a read/write attribute if its value is ever expected to change, or is it considered acceptable to override the read-only aspect of the attribute by using
$self->{age}
within theHAPPY_BIRTHDAY
function?
我不认为 $self->{age}
是一个文档化的接口,所以它甚至不能保证工作。
在这种情况下,我会使用 https://metacpan.org/pod/Moose::Manual::Attributes#Accessor-methods 中所述的私人编写器:
has 'weight' => (
is => 'ro',
writer => '_set_weight',
);
您甚至可以使用 https://metacpan.org/pod/MooseX::AttributeShortcuts#is-rwp 中的 'rwp'
自动执行此操作:
use MooseX::AttributeShortcuts;
has 'weight' => (
is => 'rwp',
);
使用 Moose 时,最佳做法是始终使用生成的访问器方法,即使在对象自己的方法中也是如此 class。以下是几个原因:
访问器方法可能会被执行特殊操作的子 class 覆盖。调用
$self->age()
确保调用正确的方法。可能有方法修饰符,例如
before
或after
,附加到属性。直接访问哈希值将跳过这些。可能有一个谓词或更清晰的方法附加到属性(例如
has_age
)。直接乱用哈希值会让他们迷惑。散列键容易出现拼写错误。如果你不小心说了
$self->{aeg}
错误将不会被立即捕获。但是$self->aeg
会死掉,因为这个方法不存在。一致性好。没有理由在一个地方使用一种风格而在其他地方使用另一种风格。它也使新手更容易理解代码。
在只读属性的特定情况下,这里有一些策略:
使您的对象真正不可变。如果您需要更改一个值,请构造一个新对象,该对象是具有新值的旧对象的克隆。
使用只读属性存储真实年龄,指定私有写法
例如:
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 );
}
更新
下面是一个示例,说明您可以如何使用惰性构建器根据一个属性设置另一个属性。
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
}
直到访问 age
才会调用惰性构建器,因此您可以确定 is_baby
会在那里。
直接设置hash元素当然会跳过builder方法
开箱即用的 perl 不是类型安全的,也没有太多封装方式,因此很容易做出鲁莽的事情。 Moose 在您的 perl 对象上强加了一些文明,用安全和稳定换取了一些自由。如果 Moose 变得太令人窒息,底层的 Perl 仍然存在,所以有办法绕过 Moose 的铁拳试图制定的任何法律。
一旦您意识到您已将属性声明为只读,但您想要更改它,即使您也说过您希望它是只读的,并且在大多数宇宙中您将某些内容声明为只读,因为您不想更改它,然后一定要继续更新 $person->{age}
。毕竟,你知道自己在做什么。