我不建议这样做。您正在引入“远距离行动”,这会导致一些非常难以捕捉的错误。用户认为他们得到了一个字符串。一个词串只能通过直接而明显地改变它来改变。它必须在适当的位置进行更改,或者显然传递给函数或附加到某物的引用。
my $text = $file->text;
say $text; # let's say it's 'foo'
...do some stuff...
$file->text('bar');
...do some more stuff...
# I should be able to safely assume it will still be 'foo'
say $text;
该代码块很容易理解,因为所有可能影响的事物$text
都立即可见。这就是词汇上下文的全部意义,隔离可以改变变量的内容。
通过返回一个可以随时改变的东西,你已经悄悄地打破了这个假设。没有任何迹象表明假设已被打破。当他们去打印$text
并得到bar
它时,发生了什么变化并不明显$text
。整个程序中的任何内容都可能发生变化$text
。那一小段代码现在变得无限复杂。
另一种看待它的方式是:Perl 中的标量变量有一个定义的接口。该界面的一部分说明了如何更改它们。您正在破坏此界面并向用户撒谎。这就是通常滥用重载/绑定变量的方式。
无论你试图解决什么问题,你都是通过增加更多的问题,通过使代码更复杂和更难理解来解决的。我会退后一步,问你想通过捆绑解决什么问题。
我会做的只是返回一个标量引用。这会提醒用户它可以随时从他们下方更改出来。掩盖一条非常重要的信息没有魔法。
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
{
package File;
use Moose;
has 'text_ref' => (
is => 'rw',
isa => 'Ref',
default => sub {
return \("");
}
);
sub BUILDARGS {
my $class = shift;
my %args = @_;
# "Cast" a scalar to a scalar ref.
if( defined $args{text} ) {
$args{text_ref} = \(delete $args{text});
}
return \%args;
}
sub text {
my $self = shift;
if( @_ ) {
# Change the existing text object.
${$self->text_ref} = shift;
return;
}
else {
return $self->text_ref;
}
}
}
my $file = 'File'->new('text' => 'hello');
my $text = $file->text();
say $$text;
$file->text('goodbye');
say $$text;
也就是说,这就是你如何做你想做的事。
我建议不要使用领带。它非常慢,比方法调用慢得多,而且很古怪。它的怪癖之一是绑定性质附加到变量本身,而不是引用的数据。这意味着您不能返回绑定变量。
相反,我建议使用重载对象来存储您更改的文本。
{
package ChangingText;
# Moose wants class types to be in a .pm file. We have to explciitly
# tell it this is a class type.
use Moose::Util::TypeConstraints qw(class_type);
class_type('ChangingText');
use overload
'""' => sub {
my $self = shift;
return $$self;
},
fallback => 1;
sub new {
my $class = shift;
my $text = shift;
return bless \$text, $class;
}
sub set_text {
my $self = shift;
my $new_text = shift;
$$self = $new_text;
return;
}
}
重载对象有它们自己的注意事项,主要是由于代码需要字符串编写类似的东西if !ref $arg
,但它们比深度关系错误更容易处理。
为了使它透明,将ChangingText 对象存储在File 对象中,然后text
在其周围放置一个手工制作的访问器来处理纯字符串。访问器确保重用相同的 ChangeText 对象。
为了完成这种错觉,BUILDARGS 用于将纯文本初始化参数更改为 ChangeText 对象。
{
package File;
use Moose;
has 'text_obj' => (
is => 'rw',
isa => 'ChangingText',
default => sub {
return ChangingText->new;
}
);
sub BUILDARGS {
my $class = shift;
my %args = @_;
# "Cast" plain text into a text object
if( defined $args{text} ) {
$args{text_obj} = ChangingText->new(delete $args{text});
}
return \%args;
}
sub text {
my $self = shift;
if( @_ ) {
# Change the existing text object.
$self->text_obj->set_text(shift);
return;
}
else {
return $self->text_obj;
}
}
}
然后它透明地工作。
my $file = File->new('text' => 'hello');
my $text = $file->text();
say $text; # hello
$file->text('goodbye');
say $text; # goodbye