在Perl中隐藏用户的tie调用
我如何隐藏对用户的“tie”调用,以便调用访问器将隐式地为用户执行该操作 我想这样做,因为我有一个用户可以访问的数据结构,但是存储在这个结构中的值可以在用户不知情的情况下修改 如果数据结构中的某个属性发生了更改,我希望引用该属性的任何变量也会被修改,以便用户始终使用新数据。因为用户总是想要新的数据,所以如果用户甚至不需要知道它正在发生,那么它就更简单、更直观 这就是我目前所拥有的。。。但它似乎不起作用,输出为:在Perl中隐藏用户的tie调用,perl,oop,tie,Perl,Oop,Tie,我如何隐藏对用户的“tie”调用,以便调用访问器将隐式地为用户执行该操作 我想这样做,因为我有一个用户可以访问的数据结构,但是存储在这个结构中的值可以在用户不知情的情况下修改 如果数据结构中的某个属性发生了更改,我希望引用该属性的任何变量也会被修改,以便用户始终使用新数据。因为用户总是想要新的数据,所以如果用户甚至不需要知道它正在发生,那么它就更简单、更直观 这就是我目前所拥有的。。。但它似乎不起作用,输出为: hello hello 我想要的是: hello goodbye 代码: ret
hello
hello
我想要的是:
hello
goodbye
代码:
return$text
只返回变量的值,而不是变量本身。您可以返回对它的引用,但是:
sub text {
my ($self) = @_;
tie my $text, 'FileText', $self;
return \$text;
}
然后,您必须使用$$text
取消对它的引用:
my $file = 'File'->new('_text' => 'hello');
my $text = $file->text();
say $$text;
$file->_text('goodbye');
say $$text;
我不建议这样做。您正在引入“远距离操作”,这会导致一些很难捕获的bug。用户认为他们得到了一个字符串。词法字符串只能通过直接和明显地改变它来改变。它必须在适当的地方被修改,或者明显地被传递到一个函数或者附加到某个东西的引用中
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
并获得条
时,不明显的是什么改变了$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
,但它们比深结bug更容易处理
要使其透明,请将ChangingText对象存储在File对象中,然后在其周围放置一个手工制作的text
accessor来处理普通字符串。访问器确保重用相同的ChangingText对象
{
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;
}
}
}
为了完成这个错觉,BUILDARGS用于将纯文本初始化参数更改为ChangingText对象
{
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
啊,这很有道理。。。嗯,但是如果我不希望返回的值需要被取消引用呢?有没有办法将别名从子例程返回到
$text
,这样就可以像访问纯标量一样访问它?如果要返回引用,就不需要绑定。只需返回引用并根据需要进行更改。这对用户来说更简单、更快、更透明。@Schwern:你如何用一个简单的引用实现子存储{die'Read only!'
?@choroba我错过了这一部分。用于根据需要打开和关闭只读标志。此解决方案添加了“远距离操作”<代码>$text可以在声明它的上下文之外更改。读者无法知道$text
可以如何更改。更糟糕的是,读者会认为$text
是一个简单的字符串,并认为它纯粹是词汇。这一切都使代码调试变得极其复杂。也许你应该问一个关于你试图用搭售来解决的问题?那太棒了!现在,我要花5个小时来思考这个过程:)我甚至不确定在12909309个不同的人告诉我这是一个可怕的做法后我是否会使用它,但感谢你为我提出的问题提供了一个有效的解决方案。