【问题标题】:Watch for changes of a Moose attribute注意 Moose 属性的变化
【发布时间】:2018-11-11 12:35:02
【问题描述】:

当属性的内容通过引用而不是通过mutator设置其值时,Moose有什么方法可以触发回调?

让我们假设以下代码:

has _changed  => ( is => 'rw' , isa=>'Bool' ) ;
has attribute => ( 
    is=>'rw', isa=>'Maybe[HashRef]', 
    default => sub { { a => 1 , b => 2 } },     
    trigger => sub { shift->_changed(1) } 
) ;

触发器按预期工作,通过 mutator 设置属性值:

$self->attribute({ a => 2 , b => 2 }) ; # OK

但是通过它的键直接设置一个值然后触发器不会触发(当然):

$self->attribute->{a} = 3 ; # KO

我放弃了创建(和比较)序列化属性内容摘要的想法,因为它可能是具有多个嵌套级别的非常巨大的 hashref,并且在每个属性访问时创建摘要会产生性能问题。

绑定的 hashref(作为属性值)可能是一种可能的解决方案? 任何想法或建议都非常感谢。

注意:包含的 hashref 的结构是未知的(我正在编写一个 ORM 类,因此该结构可能会因存储在 NOSQL db 端的文档而异)。

【问题讨论】:

  • 如果您需要使用语法$self->attribute->{a} = ??? 来更新哈希键,我认为绑定哈希是唯一(?)选项。但是,如果您可以将语法更改为$self->attribute( <args> ),其中<args> 可以是哈希引用或标量,其中attribute 现在是一种方法(不是属性),而旧的attribute 被重命名为例如_attribute,您可以避免使用绑定哈希。现在attribute 方法将检查其参数是标量还是散列引用,并相应地更新_attribute
  • @Håkon 我绝对避免通过内部引用访问属性,但我正在开发一个将被其他人使用的类,我担心如何使其健壮,因为我很确定即使不是最佳实践,一些开发人员也会使用快捷方式:-(

标签: perl moose


【解决方案1】:

一旦您直接更改哈希引用而不是使用访问器方法,Moose 就不再参与其中。让您的属性返回对绑定哈希的引用将是更改可观察哈希的唯一策略,但这并不是一个特别有吸引力的解决方案。绑定变量很少见,并且可能会在某些代码中触发错误。它们实施起来比较困难。它们意味着每次哈希访问都会产生性能开销。

强烈考虑是否可以更改设计以避免暴露内部哈希。例如。有一个只返回散列的(浅)副本的getter,以及散列中各个元素的setter。您可以使用handlestraits 机制自动生成其中一些访问器,例如见 Moose::Meta::Attribute::Native::Trait::Hash

【讨论】:

  • 或者干脆用一个对象代替散列
  • 复制构造函数 (=) 的重载能否成为监视(任何类型的)变量更改的可能解决方案?
  • @Hannibal 根据overload 文档:“简单分配不可重载”。为此,您必须使用绑定的标量。我不认为“复制构造函数”可以在这里提供帮助。
  • @Hannibal, Re“可以重载复制构造函数”,否。复制构造函数重载用于创建不可变对象。魔术变量(其中绑定变量是一种)将是通用方法。
  • 伟大的@ikegami,你解决了我的一天!!!!!! ..经过多年的 Perl 编程,这种语言永远不会让我感到惊讶.. 魔法变量是解决方案!!!
【解决方案2】:

以下方法基于Tie::Trace Perl 模块,演示了如何轻松监视 Moose 属性更改,即使通过直接访问包含的 hashref 而不是使用适当的 setter 方法进行修改。

package Test::Document ;

use Mouse ;
use Tie::Trace qw<watch> ;

has _changed => ( is => 'rw', isa => 'Bool' ) ;
...
has value => (
    is      => 'rw', isa => 'HashRef',
    default => sub { { } },
    trigger => sub { shift->_changed( 1 )  }
) ;

sub BUILD {
    my ( $self ) = @_ ;
    $self->_changed( 0 ) ; # reset flag
    watch %{ $self->{ value } } , debug=> sub {
        $self->_changed(1)
    };
    return $self ;
}

package main ;

my $doc = Test::Document->new( value => { a => 1 , b => { c => 3 } } ) ;

my $x = $doc->value->{ b }->{ e } ; # not changed

$doc->value->{ b }->{ e } = 4 ; # changed

$doc->_changed(0);
delete $doc->value->{ b }->{ e } ; # changed

$doc->_changed(0);
$doc->value({ a => 1 }) ; # changed

优点:它有效:)

缺点:递归绑定方法,在具有大量键和嵌套级别的哈希上,可能会产生性能问题。我必须做一些基准测试。

注意:我尝试使用魔法变量,但使用 sub()-&gt;{a}-&gt;{b} 之类的语法的标量上下文传播会强制触发 store 事件,即使没有(显式)分配也是如此。欢迎提出建议。

【讨论】:

  • 嗯。它不应该,因为没有存储任何内容。当我不再游荡时会调查(我身上有一条猫)。
  • 它仍在为my $x = $doc-&gt;value-&gt;{ b }-&gt;{ c };触发
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-22
  • 1970-01-01
  • 2016-01-03
  • 2023-03-31
  • 1970-01-01
  • 2018-02-15
相关资源
最近更新 更多